Issue a credential
One request, one credential, and the wallets that render asynchronously.
POST/v1/credentials
Creates the credential and moves it to issued.
Request
curl -X POST https://api.nomi-tech.com/v1/credentials \
-H "Authorization: Bearer nomi_sk_live_EXAMPLE" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order_example_12345" \
-d '{
"subjectId": "sub_…",
"templateKey": "membership_platinum",
"channels": ["apple_wallet", "google_wallet"],
"expiresAt": "2027-01-31T23:59:59Z"
}'| Field | Required | What it does |
|---|---|---|
subjectId | Yes | Who holds the credential. |
templateKey | One of the two | Uses the latest published version of that template. |
templateVersionId | One of the two | Pins a specific version. Takes precedence over templateKey. |
channels | No | apple_wallet, google_wallet, or both. Defaults to apple_wallet. |
expiresAt | No | An RFC 3339 instant. Overrides the template's validity rule. |
The response
201 Created
{
"id": "cred_…",
"serial": "…",
"state": "issued",
"fields": { "tier": "Platinum", "member": "Alex Morgan", "since": "2021-03-04" },
"subject": { "id": "sub_…", "externalId": "crm_example_001", "status": "active" },
"channels": [
{ "channel": "apple_wallet", "state": "pending", "installUrl": null },
{ "channel": "google_wallet", "state": "pending", "installUrl": null }
],
"publicId": "…",
"verificationUrl": "…",
"issuedAt": "2026-09-20T15:04:11Z",
"expiresAt": "2027-01-31T23:59:59Z"
}Retrying safely
Send an Idempotency-Key and a retry replays the first response instead of issuing twice. A request that timed out on the way back is indistinguishable from one that never arrived — this is what makes retrying it the correct thing to do. See Errors.