Verify a person — hosted page
The shortest integration: send the person to a Socious-hosted page, and read the result from your backend. Two calls, no frontend work, no QR rendering.
Use this unless you specifically need the QR code inside your own interface — in which case see the direct API flow.
1. Create the verification request
Once, in the dashboard, under Verifications. Pick the schema, add any conditions, and copy the verification’s ID. Details in Verification requests.
2. Send the person to the hosted page
https://app.shinid.com/connect/redirect/VERIFICATION_ID?customer=CUSTOMER_ID
CUSTOMER_ID is any stable identifier from your system — your internal user ID, an
application number, an account reference. It is how you will look the result up later, so
it has to be something you can reproduce. It is also stored on our side against this
person, so avoid putting anything sensitive in it.
The page shows a connection QR code. The person scans it with the Socious Wallet, approves the connection, and shares the requested credential.
The person can only share a credential they already hold. For the identity schemas that means having completed the wallet’s identity check first — and every check run in the wallet must be covered by an organization code, so if your people may arrive unverified, mint a code and include it in the ask.
Sending the same person to the same verification twice does not create a duplicate — the
individual record is keyed on (customer_id, verification_id) and reused. That makes
the redirect safe to retry, and safe to put behind a “try again” button.
3. Read the result from your backend
curl https://api.shinid.com/verifications/VERIFICATION_ID/individuals/CUSTOMER_ID \
-H "apikey: YOUR_SECRET_KEY"
{
"id": "9a1f0e9c-...",
"verification_id": "339ac66a-...",
"recipient_id": "4c2b18d7-...",
"status": "VERIFIED",
"body": { "...": "the claims the person shared" },
"issuer_did": "did:prism:5e3a2f0c...",
"validation_error": null,
"connection_at": "2026-08-05T10:18:40Z",
"verified_at": "2026-08-05T10:21:33Z",
"created_at": "2026-08-05T10:18:02Z",
"updated_at": "2026-08-05T10:21:33Z"
}
Treat the person as verified only when status is VERIFIED. body is populated on
FAILED too — the credential arrived, it just did not satisfy a condition — so the
presence of claims is not the signal. The status is.
4. Know when to look
There is no webhook for verification results yet, so your backend finds out by asking. Two patterns work:
Check on return. If the person comes back to your application after finishing (a “I’ve done it” button, or your own page they navigate back to), make the call above at that moment. This is the simplest pattern and covers most flows.
Poll a pending set. Keep your own list of individuals awaiting an answer and re-check them on a schedule — every few seconds while the person is live in your flow, then backing off to minutes. There is no rate limit on this endpoint, but do back off: a person who walked away from the QR code will never reach a terminal status, and a tight loop against them is pure waste.
If a callback would materially improve your integration, say so — it is a known gap rather than a design position.
Status values
| Status | Meaning | Terminal |
|---|---|---|
CREATED | Opened; the person has not connected a wallet yet | No |
REQUESTED | Wallet connected, proof request sent, awaiting approval | No |
VERIFIED | Credential received, issuer accepted, all conditions passed | Yes |
FAILED | Credential received, but a check failed — read validation_error | Yes |
A person who abandons the flow simply stays at CREATED or REQUESTED. Decide your own
expiry — nothing on our side moves them to FAILED.