Skip to main content

RFID Endpoints

RFID card-to-content mapping management. Base path: /toy/admin/rfid.

When an ESP32 device scans an RFID card, it calls the public lookup endpoint to find what content to play. Admin endpoints manage the mappings between RFID UIDs and content packs/questions. A RAG (Retrieval-Augmented Generation) search layer powered by Qdrant enables semantic content matching.

Endpoint Summary

Card Mappings

MethodPathAuthDescription
GET/toy/admin/rfid/card/pageAdminPaginated card mapping list
GET/toy/admin/rfid/card/listAdminAll card mappings (no pagination)
GET/toy/admin/rfid/card/lookup/:rfidUidNoneDevice lookup — find content for scanned UID
POST/toy/admin/rfid/card/rag-lookup/:rfidUidNoneRAG-enhanced card lookup
GET/toy/admin/rfid/card/uid/:rfidUidAdminGet mapping by RFID UID (admin)
GET/toy/admin/rfid/card/pack/:packCodeAdminAll cards in a pack
GET/toy/admin/rfid/card/question/:questionIdAdminAll cards for a question
GET/toy/admin/rfid/card/:idAdminGet mapping by numeric ID
POST/toy/admin/rfid/cardAdminCreate card mapping
PUT/toy/admin/rfid/cardAdminUpdate card mapping
DELETE/toy/admin/rfid/cardAdminDelete card mappings (array in body)
POST/toy/admin/rfid/card/deleteAdminDelete card mappings (POST alternative)
GET/toy/admin/rfid/mapping/optionsAdminGet all questions/packs for mapping UI

Series Lookup

MethodPathAuthDescription
GET/toy/admin/rfid/series/lookup/:uidNoneCheck if UID falls in a series range

Packs

MethodPathAuthDescription
GET/toy/admin/rfid/pack/pageAdminPaginated pack list
GET/toy/admin/rfid/pack/listAdminAll packs
GET/toy/admin/rfid/pack/activeNoneAll active packs (public)
GET/toy/admin/rfid/pack/code/:packCodeNoneGet pack by code
POST/toy/admin/rfid/packAdminCreate pack
PUT/toy/admin/rfid/packAdminUpdate pack
DELETE/toy/admin/rfid/packAdminDelete pack
MethodPathAuthDescription
POST/toy/admin/rfid/rag/searchUserSemantic search in RFID content vector DB

GET /toy/admin/rfid/card/lookup/:rfidUid

Primary device-facing endpoint. Called by the ESP32 (via the MQTT gateway or LiveKit agent) after scanning an RFID card to determine what content to play.

If the sequence query parameter is provided, uses the content pack / RAG system to return a sequentially-indexed item. Otherwise performs a direct UID lookup.

Path Parameters

ParamExampleDescription
rfidUid04:A3:B2:C1:D0:00:00RFID UID (colons/dashes optional)

Query Parameters

ParamTypeDescription
sequenceintegerOptional. If provided, returns the Nth item from the card's content pack.

Response

{
"code": 0,
"msg": "success",
"data": {
"rfidUid": "04A3B2C1D00000",
"contentType": "music",
"title": "Twinkle Twinkle",
"packName": "Nursery Rhymes Pack",
"items": [
{
"id": "uuid",
"title": "Twinkle Twinkle",
"awsS3Url": "https://cdn.example.com/music/twinkle.mp3",
"durationSeconds": 120
}
]
}
}

Returns 404 if no mapping is found for the UID.


POST /toy/admin/rfid/card/rag-lookup/:rfidUid

Enhanced lookup that performs semantic search via Qdrant when the card has a content_pack_id and a pre-computed embedding vector is provided. Also returns emotion tags extracted from the content pack.

Request

{
"embedding": [0.123, -0.456, ...],
"queryText": "nursery rhyme about stars",
"includeRag": true
}
FieldTypeDescription
embeddingnumber[]Pre-computed query embedding vector (1536 dimensions for text-embedding-ada-002)
queryTextstringOriginal query text (for logging)
includeRagbooleanWhether to include RAG results (default true)

Response

{
"code": 0,
"msg": "success",
"data": {
"rfidUid": "04A3B2C1D00000",
"contentType": "music",
"title": "Twinkle Twinkle",
"rag_results": [
{
"id": "qdrant-point-id",
"score": 0.92,
"content": "Twinkle twinkle little star...",
"title": "Twinkle Twinkle",
"category": "English",
"emotion": "happy",
"language": "en"
}
],
"emotions": ["happy", "curious"],
"emotion": "happy"
}
}

Card Mapping CRUD

GET /toy/admin/rfid/card/page

Query Parameters

ParamTypeDescription
pageintegerDefault 1
limitintegerDefault 10
rfidUidstringFilter by UID
packCodestringFilter by pack code
questionIdintegerFilter by question ID
questionPackIdintegerFilter by question pack ID
contentPackIdintegerFilter by content pack ID
cardTypestringFilter by card type
activebooleanFilter by active status

Response

{
"code": 0,
"msg": "success",
"data": {
"list": [ ... ],
"total": 150,
"page": 1,
"limit": 10,
"pages": 15
}
}

POST /toy/admin/rfid/card — Create card mapping

Request

{
"rfidUid": "04A3B2C1D00000",
"questionId": 42,
"questionIds": [42, 43, 44],
"packCode": "NURSERY_01",
"packId": 5,
"contentPackId": 12,
"notes": "Twinkle Twinkle card",
"active": true
}
FieldRequiredDescription
rfidUidYesRFID UID hex string
questionIdNoPrimary question ID
questionIdsNoMultiple question IDs
packCodeNoPack code identifier
packIdNoPack ID
contentPackIdNoContent pack ID for RAG lookup
notesNoAdmin notes
activeNoDefault true

Returns { "code": 0, "data": null } on success.

PUT /toy/admin/rfid/card — Update card mapping

Same fields as create, but id (integer) is required.

{
"id": 99,
"rfidUid": "04A3B2C1D00000",
"packCode": "NURSERY_02",
"active": false
}

Returns { "code": 0, "data": null } on success.

DELETE /toy/admin/rfid/card — Delete card mappings

Body is a raw JSON array of integer IDs:

[1, 2, 3]

Returns { "code": 0, "data": null } on success.

POST /toy/admin/rfid/card/delete

POST alternative for the delete operation (same body format: array of integer IDs). Useful for clients that cannot send a body with DELETE.


GET /toy/admin/rfid/mapping/options

Returns consolidated data for the mapping UI: all available questions, packs, question packs, and content packs.

{
"code": 0,
"msg": "success",
"data": {
"questions": [ ... ],
"packs": [ ... ],
"questionPacks": [ ... ],
"contentPacks": [ ... ]
}
}

Series Lookup

GET /toy/admin/rfid/series/lookup/:uid

Checks whether the given UID falls within any configured series range mapping. Series ranges allow a single mapping to cover a contiguous block of card UIDs (e.g. a full deck of playing cards).

Returns the series mapping object if found, 404 otherwise.


Pack Management

Packs group related RFID cards. A pack has a code, a name, and an active flag.

GET /toy/admin/rfid/pack/page

Query Parameters

ParamDescription
pageDefault 1
limitDefault 10
packCodeLIKE filter on pack code
nameLIKE filter on pack name
activeBoolean filter

GET /toy/admin/rfid/pack/active

Public endpoint. Returns all active packs. No authentication required.

GET /toy/admin/rfid/pack/code/:packCode

Returns a single pack by its code. No authentication required.

POST /toy/admin/rfid/pack — Create pack

{
"packCode": "NURSERY_01",
"name": "Nursery Rhymes",
"description": "Classic nursery rhymes for young children",
"active": true
}

POST /toy/admin/rfid/rag/search

Direct semantic search in the RFID content Qdrant vector database. Requires a pre-computed embedding vector.

Request

{
"embedding": [0.123, -0.456, ...],
"contentPackId": 12,
"language": "en",
"limit": 5,
"scoreThreshold": 0.7
}
FieldTypeRequiredDescription
embeddingnumber[]YesQuery embedding vector (1536 dimensions for ada-002)
contentPackIdintegerNoFilter results to a specific content pack
languagestringNoFilter by language code
limitintegerNoMax results (default 5)
scoreThresholdnumberNoMinimum similarity score 0–1 (default 0.7)

Response

{
"code": 0,
"msg": "success",
"data": [
{
"id": "qdrant-point-id",
"score": 0.94,
"payload": {
"title": "Twinkle Twinkle",
"content": "Twinkle twinkle little star...",
"language": "en",
"emotion": "happy",
"contentPackId": 12
}
}
]
}