Using externalId for Correlation
externalId is your identifier — a user ID, order ID, KYC case number, or transaction reference from your own system. Pass it when creating an offer or session, and iDen2 echoes it back verbatim in every response and callback (on Credential Factory, one webhook names the field differently and one omits it — see which).
Why it exists
Without externalId, correlating an iDen2 flow back to your business record requires persisting iDen2's UUIDs (offerId, sessionId) as lookup keys in your own tables. That works, but it inverts the relationship: your domain records end up keyed by another system's identifiers.
With externalId, every offer and session response and callback already carries your reference, so any consumer of that data — a webhook handler, a log pipeline, a support engineer reading a session dump — can find the corresponding record in your system without a join table.
Credential Factory webhook fields
Credential Factory webhooks don't all name the field externalId. Read your reference from the field for the event:
event | Your externalId value is in |
|---|---|
issuance_completed | userId |
issuance_failed, wallet_accepted, wallet_rejected | externalId |
revocation | Not included — the payload carries credentialId, reason, revokedAt and status, but no externalId or offerId. Store the credentialId from issuance_completed so you can match it. |
Verification Gateway webhooks always carry externalId.
How to use it
Set it at creation time on either API:
// Credential Factory — POST /api/v1/iaas/credential-offers
{ "credentialData": { "...": "..." }, "externalId": "order-12345" }
// Verification Gateway — POST /api/v1/verifier/sessions
{ "externalId": "kyc-check-99887", "ttlSeconds": 120 }
It comes back everywhere:
- the
201creation response, - every
GET /offers/{offerId}/GET /sessions/{sessionId}poll, - webhook callback payloads (if configured) — on Credential Factory, check the field name per event in the table above.
On Verification Gateway, externalId is limited to 256 characters.
Practical guidance
- Still store
offerId/sessionIdon your record after creation — you need them to poll.externalIdsaves you the reverse lookup, not the forward one. - Make it meaningful but not sensitive. It transits and is stored alongside the flow; use an opaque internal reference (
kyc-check-99887), never PII like an email address or national ID number. - Make it unique per flow if you ever create multiple offers/sessions per business record (retries after expiry, for instance) — e.g.
order-12345-attempt-2. iDen2 does not enforce uniqueness; it's pure pass-through. - Validate it on the way back in. When a webhook arrives, treat
externalIdlike any untrusted input: look it up, confirm the record exists and is in a state expecting this callback, and confirm the result viaGET /sessions/{sessionId}before acting (Polling vs. Webhooks).
Example: webhook handler keyed by externalId
app.post('/iden2-callback', async (req, res) => {
const {sessionId, externalId} = req.body;
const kycCase = await db.kycCases.findByReference(externalId);
if (!kycCase || kycCase.status !== 'awaiting_verification') {
return res.sendStatus(200); // unknown or stale — acknowledge and ignore
}
// Source of truth: confirm with our own credentials
const session = await verificationGateway.getSession(sessionId);
if (session.status === 'completed' && isFullyVerified(session.verificationResult)) {
await db.kycCases.markVerified(kycCase.id, session.credentialData);
}
res.sendStatus(200);
});