Socious
Account

Organization codes

Minting needs a paid plan. Phone and address coverage needs Premium+.

Every verification a person runs in the Socious Wallet — the identity check included — must be covered by an organization. An organization code is how you cover it: you mint a code, your people type it into the wallet, and each run is attributed to your account and counted against your allowance. Without a code the wallet refuses to start the run at all, so when you ask a membership, a cohort or an electorate to get verified, the code is part of the ask — not an optional way to pick up the bill.

What a code can cover

credential_types names the checks a code covers, and your plan decides what you can grant:

  • identity — any paid plan. It is also always included, whatever you send: it is the check the wallet requires a code for in the first place, so a code that could not cover it would be a code that covers nothing.
  • phone and address — Premium+. These spend real money per run, so they need the tier that pays for them.

Minting narrows to your plan rather than refusing: a Standard organization asking for all three types gets a working identity-only code, and the stored credential_types come back in the response, so the narrowing is visible rather than silent.

Minting a code

curl -X POST https://api.shinid.com/organizations/ORG_ID/codes \
  -H "Authorization: Bearer SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Governance round, August",
    "credential_types": ["identity", "phone", "address"],
    "max_redemptions": 500,
    "expires_at": "2026-09-30T23:59:59Z"
  }'
FieldNotes
credential_typesOne or more of identity, phone, address. Anything else is rejected; what is stored is narrowed to your plan
max_redemptionsOmit for uncapped. Set it. An uncapped code is an unbounded authority to spend on your account
expires_atOptional, must be in the future
labelOptional, for your own dashboard

The response includes code and display_code — the same value, plain and grouped (SHN7K4M9XQ2TV and SHN-7K4M-9XQ2TV), and the grouped form is the one to put in front of a human. Typing is forgiving: case, dashes and spaces do not matter, and I, L and O are read as 1, 1 and 0, so a code transcribed from a projector still lands.

GET /organizations/{id}/codes lists them with redemption_count, active and, when inactive, inactive_reason. DELETE /organizations/{id}/codes/{code_id} revokes one immediately.

What the person does

They open the Socious Wallet, type the code, and run the verification — or, better when you can put a URL in front of them, they click a link that already carries it:

https://wallet.socious.io/verify?code=SHN-7K4M-9XQ2TV&tier=kyc_phone

The wallet reads ?code= off the link, keeps it through onboarding for someone who has no wallet yet, and redeems it exactly as if it had been typed — same code, no transcription. This is how verify-demo.socious.io sends its members over; the link builder is quoted in the API reference.

tier names the check bundle the run performs, as ONE combined session rather than one run per check:

tierThe person completesCode must cover
standard (default)ID document + livenessidentity
kyc_phoneID document + liveness + phoneidentity, phone
premium_plusID document + liveness + phone + proof of addressidentity, phone, address

The wallet runs only what is missing: a person who already holds a valid identity credential and follows a kyc_phone link is taken straight to the phone step — their face never goes through the ID check twice, which matters because a second identity run on the same person is flagged as a duplicate and declined. A person who already holds everything the tier asks for is simply shown as verified, and nothing is spent.

Either way, the wallet exchanges the code behind the scenes:

curl -X POST https://api.shinid.com/vouchers/exchange \
  -H "Content-Type: application/json" \
  -d '{ "code": "SHN-7K4M-9XQ2TV", "did": "did:prism:...", "credential_types": ["identity"] }'

You will not normally call this yourself. It is worth knowing what it enforces:

  • The code is checked live. Revoked, expired and exhausted codes are refused, and so is a code whose organization has since left its paid plan. A code never outlives the entitlement behind it.
  • The resulting grant is bound to the DID that redeemed it, and is single-use. A grant lifted off the wire cannot be replayed by another wallet.
  • credential_types narrows what this run needs. It can never widen past what the code allows.
  • The response carries your organization_name, so the wallet can tell the person who is covering their verification.
  • The endpoint takes no authentication, since a wallet holder has no account with us. It is throttled to 200 attempts per hour per IP — sized so a whole room onboarding behind one office network does not lock itself out.

A code that covers identity but not the paid modules still verifies the person’s identity: the wallet runs the parts the grant covers and shows the rest as locked, rather than failing the person outright.

The two halves of the journey — acquiring the credential (which the code covers) and presenting it to your verification — compose into one link. Append the code and the tier to the person’s connection link from the direct API flow:

{connection_url}?code=SHN-7K4M-9XQ2TV&tier=kyc_phone

where connection_url came from POST /verifications/individuals (with customer_id = your user ID) followed by GET /verifications/{individual_id}/connect. The redirect carries the appended parameters through to the wallet, which stores the proof request, walks the person through whatever the tier asks for and they do not yet hold — including onboarding, for someone with no wallet at all — and then presents to your verification. You poll GET /verifications/{verification_id}/individuals/{customer_id} exactly as in the two-trip flow; the result stays attached to your user ID.

This is the link shape verify-demo.socious.io mints per member, so you can walk the whole journey there before building your own.

One requirement on your side: the verification’s conditions must only test what the tier actually runs. A kyc_phone run issues a credential with address_verified as false (the address step never ran), so a condition on address_verified would fail every member — condition on liveness_verified and phone_verified only.

A code is typed by a person into their wallet, with no account and no link to click. That suits an announcement, a printed notice, a room full of people. Use the one-link journey above when you are addressing one identified person and can put a URL in front of them — it is the same connection link a hosted verification page renders as a QR code, with acquisition covered in the same trip instead of being a separate ask.

Direct vouchers

POST /vouchers mints the same grant directly, without a code, for a member of a Premium+ organization. You get back a signed token and a wallet_url, and you deliver it however you like. Codes are for people, vouchers for systems.