eID Overview
eID is CAKE's chip-reading identity service: the Mobile SDK reads the contactless chip inside a Vietnamese NID — National Identity Card, known locally as Căn cước công dân (CCCD) or Thẻ căn cước (TCC), and the platform parses the ICAO data groups, returns the cardholder's identity fields, and verifies them against the C06 national police database. This page covers the transaction lifecycle, the two authentication schemes, and the end-to-end flow.
INFO
Every sample in this documentation uses the same fictional identity — Nguyễn Văn A, NID 001xxxxxxxxx, user_id user_00000001, ref_id ref_00000001. Credentials, tokens and keys appear as <PLACEHOLDER>. None of it is real data; replace it all with your own values.
Prerequisites
Before calling any endpoint you need a tenant provisioned by CAKE and the credentials issued with it.
| What | Description |
|---|---|
| Access ID | A UUID that uniquely identifies your Tenant. Sent in the Authorization header. Provided by CAKE. |
| Access Key | The secret key used to sign every request and to verify every response signature. Provided by CAKE. |
| Base URL | Issued by CAKE together with your credentials, for both stage and production. |
| Mobile SDK | The CICReader SDK, embedded in your iOS or Android app, performs the physical chip read. |
| NFC-capable device | The end user's phone must have an NFC reader and the card must be a chip-enabled NID. |
Contact CAKE Support to obtain access_id and access_key.
Samples throughout this documentation write the base URL as <BASE_URL> — substitute the value above.
Core Concepts
Transaction Lifecycle
Every chip read happens inside a transaction, which moves through three states.
| Field | Type | Description |
|---|---|---|
NOT_VERIFIED | string | The transaction has been created and a token issued, but the chip data has not yet been verified against C06. |
VERIFIED | string | The chip data was read and confirmed against C06. |
EXPIRED | string | The transaction was not completed before its token expired. |
Four endpoints move a transaction through that lifecycle:
| What | Description |
|---|---|
| Create Transaction | Your backend opens a transaction and receives the JWT the SDK will use. |
| Read NFC | The SDK submits the raw chip data groups and receives the parsed identity fields plus a verification_key. |
| Verify NFC | Your backend verifies the read against C06 and closes the transaction. |
| Get NFC | Retrieves the stored raw and parsed chip data for a document. |
Authentication
eID uses two schemes, and which one you use depends on the endpoint, not just the caller.
| What | Description |
|---|---|
HMAC — Authorization: ATV <ACCESS_ID>:<SIGNATURE> | Used by your backend on Create Transaction and Verify NFC. |
JWT — Authorization: Bearer {token} | Used by the Mobile SDK on Read NFC and Get NFC. The token comes from Create Transaction. |
WARNING
The Access Key is a server-side secret. Never embed it in a mobile app or any client the end user controls — the SDK is given a per-transaction JWT precisely so the signing key never leaves your backend.
Option 1: HMAC authentication (backend to eID backend)
Server-to-server requests are authenticated with HMAC-SHA256, which proves both the integrity and the authenticity of the request body using a secret shared by your backend and CAKE.
Two headers are required:
| Header | Description |
|---|---|
X-TV-Timestamp | Current timestamp in RFC3339 format. Example: 2019-04-21T18:00:15+07:00 |
Authorization | ATV <ACCESS_ID>:<SIGNATURE> |
The signature is a Base64-encoded HMAC-SHA256 of a canonical StringToSign:
StringToSign = HttpMethod + "\n" + UrlPath + "\n" + Timestamp + "\n" + HashedBody
# HttpMethod: POST, GET, PUT, PATCH or DELETE
# UrlPath: relative route path, excluding the domain. Example: /v1/create_transaction
# Timestamp: request time in RFC3339 format; must match the X-TV-Timestamp header
# HashedBody: hex_encode(SHA256(raw body))
# Example (body `{}`): 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
Signature = Base64(HmacSha256(AccessKeySecret, Utf8EncodingOf(StringToSign)))Reference implementation in JavaScript:
const CryptoJS = require("crypto-js");
const timestamp = new Date().toISOString(); // 2024-05-09T07:53:10.591Z
const access_key = "<ACCESS_KEY>";
const path = "/v1/create_transaction";
const body = `{
"document_id": "001xxxxxxxxx",
"ref_id": "ref_00000001"
}`;
function tokenize(method, path, timestamp, body, key) {
const hashed_body = CryptoJS.SHA256(body).toString(CryptoJS.enc.Hex);
const formatted = `${method}\n${path}\n${timestamp}\n${hashed_body}`;
const hmac = CryptoJS.HmacSHA256(formatted, key);
const hashed = hmac.toString(CryptoJS.enc.Hex);
return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Hex.parse(hashed));
}
const authorize_token = tokenize("POST", path, timestamp, body, access_key);
// Send `timestamp` as X-TV-Timestamp and `authorize_token` as the signature — never log them.Sample signed request:
curl --location '<BASE_URL>/v1/create_transaction' \
--header 'Authorization: ATV <ACCESS_ID>:<SIGNATURE>' \
--header 'X-TV-Timestamp: <TIMESTAMP>' \
--header 'Content-Type: application/json' \
--header 'X-Request-Id: <RANDOM_UUID>' \
--header 'X-Client-ID: <CLIENT_ID>' \
--data '{
"document_id": "001xxxxxxxxx",
"ref_id": "ref_00000001"
}'INFO
The StringToSign uses the hex-encoded SHA256 of the raw body. Some CAKE tenants are provisioned with a Base64-encoded body hash instead — confirm which variant your tenant uses with the CAKE team before implementing.
Option 2: JWT token (Mobile SDK to eID backend)
Create Transaction returns a JWT token scoped to one transaction, one user and one ref_id. The SDK presents it as a bearer token, and it stops working once it expires.
Treat the token as opaque: pass it to the SDK as-is, and never parse, log or store it.
Response Signing
Every response from the NFC backend is signed with HMAC-SHA256 over the method, path, timestamp and hashed response body, so your backend can prove the payload really came from CAKE.
| Header | Description |
|---|---|
X-Signature-Timestamp | Signed timestamp in RFC3339 format. Example: 2019-04-21T18:00:15+07:00 |
X-Signature | Base64 HMAC token from the CAKE server. Example: <SIGNATURE> |
StringToSign = HttpMethod + "\n" + UrlPath + "\n" + Timestamp + "\n" + HashedBody
# HttpMethod: the method of the API that was called
# UrlPath: relative route path, excluding the domain
# Timestamp: signed time in RFC3339 format, sent in X-Signature-Timestamp
# HashedBody: hex_encode(SHA256(raw response body))
Signature = Base64(HmacSha256(AccessKeySecret, Utf8EncodingOf(StringToSign)))WARNING
Recompute and compare X-Signature before you trust is_verified. An unverified response is an unauthenticated one, and the C06 verdict drives your onboarding decision.
How It Works
A full NFC session spans four actors: the mobile app with the embedded SDK, your backend, the eID backend, and the C06 national police database.
sequenceDiagram
autonumber
participant App as Mobile App / SDK
participant BE as Your Backend
participant NFC as eID Backend
participant C06 as C06 Database
Note over BE,NFC: Phase 1 — Open the transaction
BE->>NFC: POST /v1/create_transaction (HMAC signed)
NFC-->>BE: id, status NOT_VERIFIED, token (JWT)
BE-->>App: token
Note over App,NFC: Phase 2 — Read the chip
App->>App: User taps NID; SDK reads SOD, DG1, DG2, DG13, DG14, DG15
App->>NFC: POST /v1/read_nfc (Bearer token)
NFC-->>App: citizen fields, face_image, verification_key
App-->>BE: verification_key
Note over BE,C06: Phase 3 — Verify against C06
BE->>NFC: POST /v1/verify_nfc (HMAC signed)
NFC->>C06: Cross-check chip data
C06-->>NFC: Match result
NFC-->>BE: is_verified, verified_at; status VERIFIED
Note over BE,NFC: Phase 4 — Retrieve stored data
BE->>NFC: POST /v1/get_nfc
NFC-->>BE: nfc_raw (data groups) + nfc_data (parsed fields)
Phase 1 — Open the transaction
- Your backend calls
POST /v1/create_transactionwith the citizen'sdocument_idand your ownref_id, signed with HMAC. - eID returns the transaction
id, astatusofNOT_VERIFIED, and atoken— the JWT the SDK will authenticate with. - Your backend passes the token down to the mobile app. It never passes the Access Key.
Phase 2 — Read the chip
- The user taps their NID against the phone. The SDK performs the ICAO chip read and extracts the data groups:
sod,dg1,dg2,dg13,dg14anddg15. - The app calls
POST /v1/read_nfcwith those data groups, authenticating with the bearer token. - eID parses the groups and returns the cardholder's identity fields, the
face_imagefrom DG2, and averification_key. - The app hands the
verification_keyback to your backend. This key is what lets your backend prove the read happened, without ever trusting the app's own claims about it.
Phase 3 — Verify against C06
- Your backend calls
POST /v1/verify_nfcwithdocument_id,ref_idandverification_key, signed with HMAC. - eID cross-checks the chip data against the C06 national police database.
- The response carries
is_verifiedandverified_at, and the transaction moves toVERIFIED. This server-side result — not theis_verifiedthe SDK saw in step 6 — is the one your decision logic should use.
Phase 4 — Retrieve stored data
POST /v1/get_nfcreturns bothnfc_raw(the original data groups) andnfc_data(the parsed identity fields) for thedocument_idsubmitted in the same transaction.
Shared Response Envelope
Every endpoint returns the same outer shape.
{
"success": true,
"code": 0,
"message": "success",
"request_id": "133963a4-3d2c-4a85-b0a1-3fa2878b8f99",
"data": {}
}| Field | Type | Description |
|---|---|---|
success | bool | Whether the call succeeded. |
code | int | The error code. 0 means no error; the range runs to 999999. |
message | string | Status message. |
request_id | string | The server request ID, used for tracing. Max length 64. |
data | object | Endpoint-specific payload. |
Note that request_id sits at the top level on eID endpoints, not inside data as it does on the eKYC image APIs.
Error Codes
| Code | Description |
|---|---|
2001002 | internal_bad_gateway |
2001010 | The API key is incorrect. |
2001019 | Transaction / reference ID not found. |
2001020 | Transaction lock failed. |
2001021 | Invalid transaction request. |
2001022 | Transaction already verified. |
2001023 | Invalid transaction key. |
2001027 | Token invalid. |
2001029 | NFC info not found. |
2001030 | Invalid document id. |
4001000 | Input parameter violates a constraint. |
4291000 | The number of requests exceeded throughput limit. |
5001000 | internal_server_error — some unexpected error occurs while processing the request. |
HTTP status codes
| Code | Description |
|---|---|
401 | You are not authorized to perform the action. |
500 | Some unexpected error occurs while processing the request. |
Next Steps
- Create Transaction — open a transaction and mint the SDK token
- Read NFC — submit the chip data groups and get the parsed identity
- Verify NFC — confirm the read against the C06 database
- Get NFC — retrieve stored raw and parsed chip data
- NID Field Definitions — every identity, address, issuance, QR and MRZ field, with formats, validation and merge rules
- Error Codes — the full error code reference across the API and Mobile SDK
- Android SDK UI Customization — restyle the CICReader scanner screen
- Get Transaction — read a transaction's current status
- Verify Transaction Authentication Key — redeem the key and close the transaction