GoIdentity API Integration
Create verification checks from websites, CRM platforms, SAP workflows, or other backend systems without opening the GoIdentity dashboard.
Overview
Use the integration API when your own platform needs to initiate identity verification checks programmatically. The API uses the verification services and questionnaire configured on the SDK integration in GoIdentity; request payloads cannot override paid checks.
| Capability | Endpoint or feature |
|---|---|
| Create a verification | POST /integrations/v1/verifications |
| Poll current status | GET /integrations/v1/verifications/:id/status |
| Retrieve a single verification | GET /integrations/v1/verifications/:id |
| List verifications | GET /integrations/v1/verifications?limit&page |
| Receive completed outcomes | verification.completed webhook |
Before You Start
Gather all of the following first — most integration friction comes from a missing credential, an unconfigured service, or an empty balance.
| Requirement | Purpose |
|---|---|
| GoIdentity dashboard account | Create integrations, configure services, view results. |
Client key + secret (gidck_…, gidcs_…) | Server-to-server credentials sent on every request. Never expose the secret in a browser or client app. |
| Verification services configured on the integration | Which checks run (ID + face, sanctions/PEP, location, questionnaire). Enforced server-side; requests cannot change them. |
| Sufficient organisation balance | Checks are billed. An empty balance returns INSUFFICIENT_BALANCE (402). |
| Webhook endpoint (HTTPS) + signing secret | Recommended. Receive completion events instead of polling. Configure the URL and generate the secret in the dashboard. |
| Sandbox / dev credentials | Run integration smoke tests before going live. |
Architecture
Your backend initiates a check; the applicant completes it in the GoIdentity mobile app; GoIdentity runs the verification pipeline asynchronously and notifies you when it finishes.
Your backend
-> POST /integrations/v1/verifications (create the check)
-> GoIdentity emails the applicant an invite / one-time code
-> applicant completes the checks in the GoIdentity mobile app
(document capture, face match, location, questionnaire, ...)
-> GoIdentity runs the verification pipeline (asynchronous)
-> the verification reaches a terminal status
-> GoIdentity POSTs a signed "verification.completed" webhook
-> (optional) you can GET /verifications/:id/status at any timeWhat runs where
- Your backend: stores the client key + secret, creates verifications, and hosts the webhook endpoint that receives outcomes.
- GoIdentity API:authenticates the credentials, applies the integration’s configured services, creates the check, and delivers the signed webhook.
- GoIdentity mobile app + pipeline: where the applicant captures their document and face, and where the asynchronous verification actually happens.
New and only becomes a terminal status after the applicant completes the mobile flow and the pipeline finishes. Design for asynchronous completion — rely on the webhook, not a synchronous response.Quickstart
- Create an SDK integration in the dashboard (one integration powers the SDK, the API, and webhooks).
- Configure the verification services and optional questionnaire on it — these are enforced on every API request.
- Copy the client key (
gidck_…) and client secret (gidcs_…); store the secret in your backend secrets manager. - Top up the organisation balance so checks can be created.
- (Recommended) Set a webhook URL, generate a signing secret, and choose whether to include KYC data / the PDF report.
- Create a verification with
POST /integrations/v1/verifications. - Receive the
verification.completedwebhook (or poll the status endpoint). - Verify the webhook signature, dedupe on the event id, and persist the outcome against your
externalReference. - Smoke-test in sandbox, then switch to production credentials and URLs.
Authentication
Every request must include the client key and client secret generated for the active SDK integration. Credentials are scoped to the owning organisation and all created checks are traceable to that integration. There is no token-exchange step — send both headers on every request.
X-GoIdentity-Client-Key: gidck_CLIENT_KEY_ID
X-GoIdentity-Client-Secret: gidcs_CLIENT_SECRET
Content-Type: application/json- Store
gidcs_secrets only in backend secrets storage. - Rotate secrets from the GoIdentity dashboard if they are exposed.
- Disabled integrations cannot create or retrieve verification checks (
INTEGRATION_DISABLED, 403).
Create Verification
Create a verification from core customer details. The optional externalReference should be your stable CRM, order, or user identifier for reconciliation.
| Field | Required | Notes |
|---|---|---|
externalReference | No | Up to 200 characters. Echoed back in status and webhooks. |
fullName | Yes | Customer legal name. |
dateOfBirth | Yes | ISO date (YYYY-MM-DD), must be in the past. |
email | Yes | Used for the verification record and dashboard traceability. |
contactNumber | Yes | Prefer E.164 format with country code. |
countryCode | No | ISO 3166-1 alpha-2 where available. |
POST https://api.dev.goidentity.com/integrations/v1/verifications
X-GoIdentity-Client-Key: gidck_CLIENT_KEY_ID
X-GoIdentity-Client-Secret: gidcs_CLIENT_SECRET
Idempotency-Key: order-12345
Content-Type: application/json
{
"externalReference": "crm-contact-12345",
"fullName": "Jane Doe",
"dateOfBirth": "1990-04-12",
"email": "jane@example.com",
"contactNumber": "+447700900123",
"countryCode": "GB"
}HTTP/1.1 201 Created
{
"verificationId": "9c0f38d4-8f12-48e7-a762-7710939d7c55",
"externalReference": "crm-contact-12345",
"status": "New",
"createdOn": "2026-06-18T11:30:00Z",
"statusUrl": "/integrations/v1/verifications/9c0f38d4-8f12-48e7-a762-7710939d7c55/status"
}Idempotency-Key header to make retries safe: repeating a create with the same key for the same integration returns the original verification instead of creating a duplicate or charging the balance twice.Lifecycle & Status
A verification moves through a non-terminal phase while the applicant completes their checks, then settles on exactly one terminal status.
| Status | Terminal? | Meaning |
|---|---|---|
New | No | Created; the applicant has not started yet. |
InProgress | No | The applicant is completing checks / the pipeline is running. |
Pass | Yes | Verification succeeded. |
Fail | Yes | Verification failed. |
ReviewRequired | Yes | Needs manual review before an outcome is trusted. |
Advisory | Yes | Completed with advisory notes to consider. |
Cancelled | Yes | The check was cancelled and will not complete. |
verification.completed webhook fires once, when the check first reaches any terminal status.Status Polling
Poll the status endpoint when your platform needs the latest known state. Access is restricted to checks created by the authenticated integration and organisation. Prefer webhooks; if you poll, back off and stop once the status is terminal.
GET https://api.dev.goidentity.com/integrations/v1/verifications/9c0f38d4-8f12-48e7-a762-7710939d7c55/status
X-GoIdentity-Client-Key: gidck_CLIENT_KEY_ID
X-GoIdentity-Client-Secret: gidcs_CLIENT_SECRETHTTP/1.1 200 OK
{
"verificationId": "9c0f38d4-8f12-48e7-a762-7710939d7c55",
"externalReference": "crm-contact-12345",
"status": "Pass",
"resultReason": "Identity checks completed successfully.",
"createdOn": "2026-06-18T11:30:00Z",
"updatedOn": "2026-06-18T11:42:10Z",
"completedOn": "2026-06-18T11:42:10Z",
"fullName": "Jane Doe",
"dateOfBirth": "1990-04-12",
"email": "jane@example.com",
"contactNumber": "+447700900123",
"countryCode": "GB",
"includeVerifications": ["id", "location", "pep"]
}Webhooks
Webhooks are configured per SDK integration. GoIdentity sends one verification.completed event when a partner-created verification first reaches a terminal status.
| Setting | Purpose |
|---|---|
WebhookUrl | HTTPS endpoint that receives completion events. |
WebhookEnabled | Controls delivery for the integration. |
WebhookIncludeKycData | Includes the applicant fields in a kycData object. |
WebhookIncludePdf | Includes a base64 PDF report in a pdfReport object. |
{
"eventId": "evt_5cf17e5d0dfd4cf88950dd0e1cebf00a",
"eventType": "verification.completed",
"createdOn": "2026-06-18T11:42:11Z",
"verification": {
"verificationId": "9c0f38d4-8f12-48e7-a762-7710939d7c55",
"externalReference": "crm-contact-12345",
"status": "Pass",
"resultReason": "Identity checks completed successfully.",
"createdOn": "2026-06-18T11:30:00Z",
"updatedOn": "2026-06-18T11:42:10Z",
"completedOn": "2026-06-18T11:42:10Z"
},
"kycData": {
"fullName": "Jane Doe",
"dateOfBirth": "1990-04-12",
"email": "jane@example.com",
"contactNumber": "+447700900123",
"countryCode": "GB",
"includeVerifications": ["id", "location", "pep"]
},
"pdfReport": {
"fileName": "KYC verification report - 9c0f38d4-8f12-48e7-a762-7710939d7c55.pdf",
"contentType": "application/pdf",
"contentBase64": "JVBERi0xLjQK..."
}
}Retry Behaviour
Any non-2xx response or network timeout is retried with exponential backoff (up to 5 attempts). Delivery attempts, response status, response body, and error messages are retained for traceability and visible in the dashboard delivery log. Acknowledge fast with a 2xx and process asynchronously.
Attempt 1: immediate
Attempt 2: about 1 minute later
Attempt 3: about 2 minutes later
Attempt 4: about 4 minutes later
Attempt 5: about 8 minutes laterSignature Verification
GoIdentity signs the raw request body with HMAC-SHA256. Verify the signature before trusting or storing webhook data.
| Header | Value |
|---|---|
X-GoIdentity-Event-Id | Unique event id. Store it for idempotency. |
X-GoIdentity-Timestamp | Unix timestamp used in the signed payload. |
X-GoIdentity-Signature | sha256=<hex digest> |
import crypto from "node:crypto";
function verifyGoIdentitySignature({ secret, timestamp, body, signature }) {
const expected =
"sha256=" +
crypto
.createHmac("sha256", secret)
.update(timestamp + "." + body)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature),
);
}timestamp + "." + rawBody. Use the raw request body (not a re-serialized object), reject old timestamps, and compare signatures with a constant-time function.Code Examples
Create a verification
curl -X POST https://api.dev.goidentity.com/integrations/v1/verifications \
-H "X-GoIdentity-Client-Key: gidck_CLIENT_KEY_ID" \
-H "X-GoIdentity-Client-Secret: gidcs_CLIENT_SECRET" \
-H "Idempotency-Key: order-12345" \
-H "Content-Type: application/json" \
-d '{
"externalReference": "crm-contact-12345",
"fullName": "Jane Doe",
"dateOfBirth": "1990-04-12",
"email": "jane@example.com",
"contactNumber": "+447700900123",
"countryCode": "GB"
}'Poll status
// Node — poll status (prefer webhooks; poll only as a fallback)
async function getStatus(verificationId) {
const res = await fetch(
`https://api.dev.goidentity.com/integrations/v1/verifications/${verificationId}/status`,
{
headers: {
"X-GoIdentity-Client-Key": process.env.GOIDENTITY_CLIENT_KEY,
"X-GoIdentity-Client-Secret": process.env.GOIDENTITY_CLIENT_SECRET,
},
},
);
const check = await res.json();
// New | InProgress | Pass | Fail | ReviewRequired | Advisory | Cancelled
return check.status;
}
// Back off between polls (e.g. every 30s) and stop once the status is terminal.Receive & verify a webhook
// Node / Express — receive and verify a webhook
import express from "express";
import crypto from "node:crypto";
const app = express();
app.post(
"/webhooks/goidentity",
// Capture the RAW body — the signature is computed over the exact bytes.
express.raw({ type: "application/json" }),
(req, res) => {
const signature = req.header("X-GoIdentity-Signature") ?? "";
const timestamp = req.header("X-GoIdentity-Timestamp") ?? "";
const eventId = req.header("X-GoIdentity-Event-Id") ?? "";
const rawBody = req.body.toString("utf8");
const expected =
"sha256=" +
crypto
.createHmac("sha256", process.env.GOIDENTITY_WEBHOOK_SECRET)
.update(timestamp + "." + rawBody)
.digest("hex");
const valid =
signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
if (!valid) return res.status(400).send("bad signature");
// Replay protection: reject deliveries with an old timestamp.
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300)
return res.status(400).send("stale timestamp");
const event = JSON.parse(rawBody);
// Idempotency: skip an eventId you have already processed, then handle it.
// await onceByEventId(eventId, () => persist(event.verification));
res.sendStatus(200); // ACK fast; do heavy work asynchronously.
},
);Environments
INVALID_CLIENT_CREDENTIALS.| Environment | API base URL | Use for |
|---|---|---|
| Sandbox / Dev | https://api.dev.goidentity.com | Integration testing with development credentials. |
| Production | https://api.goidentity.com | Live checks with production credentials. |
Troubleshooting
| Problem | Likely cause | Fix |
|---|---|---|
401 INVALID_CLIENT_CREDENTIALS | Wrong key/secret, or mixing dev and prod credentials. | Check both headers and confirm you are calling the matching environment base URL. |
403 INTEGRATION_DISABLED | The integration is disabled in the dashboard. | Re-enable the SDK integration and retry. |
402 INSUFFICIENT_BALANCE | The organisation balance cannot cover the check. | Top up the balance, then create the verification again. |
400 VALIDATION_FAILED | A field is missing or invalid. | Read the details map and correct the named fields. |
| Verification stays in New / InProgress | The applicant has not finished the mobile flow yet — this is expected. | Wait for the webhook; the status settles once the pipeline completes. |
| Webhook never arrives | Webhooks disabled, non-HTTPS URL, or your endpoint is not returning 2xx. | Enable webhooks, use an HTTPS URL, and check the delivery log in the dashboard for the response we received. |
| Signature does not match | Verifying against a re-serialized body, the wrong secret, or the wrong signed string. | Use the raw body and sign timestamp + "." + rawBody with the current secret. |
| Duplicate webhook deliveries | Your endpoint returned a non-2xx, so delivery was retried. | Return 2xx quickly and dedupe on X-GoIdentity-Event-Id. |
Error Reference
API errors use a consistent JSON shape with an actionable code, message, and optional field details.
HTTP/1.1 400 Bad Request
{
"code": "VALIDATION_FAILED",
"message": "The verification request is invalid.",
"details": {
"fullName": "fullName is required.",
"dateOfBirth": "dateOfBirth must be in the past."
}
}| Code | HTTP | Action |
|---|---|---|
INVALID_CLIENT_CREDENTIALS | 401 | Check the client key and secret for the environment. |
INTEGRATION_DISABLED | 403 | Enable the SDK integration in the dashboard. |
VALIDATION_FAILED | 400 | Correct the request fields shown in details. |
VERIFICATION_NOT_FOUND | 404 | Confirm the verification id belongs to this integration. |
INSUFFICIENT_BALANCE | 402 | Top up the organisation balance before retrying. |
Security
- Keep client secrets and webhook secrets out of frontend code.
- Use HTTPS webhook URLs only.
- Verify webhook signatures using the raw request body.
- Store event ids and process webhooks idempotently.
- Log verification ids and external references, not raw PII.
- Use separate credentials for sandbox and production.
- Rotate credentials immediately after suspected exposure.
Go Live Checklist
- Production client key and secret stored in a secrets manager (never client-side).
- Organisation balance funded so checks are not blocked.
- Verification services and questionnaire configured on the production integration.
- Webhook URL is HTTPS and reachable from the public internet.
- Webhook signing secret generated and stored; signature verified over the raw body.
- Event-id idempotency implemented so retries are not double-processed.
- All documented error codes handled (400, 401, 402, 403, 404).
externalReferenceset on every create for reconciliation.- Base URL switched to
https://api.goidentity.comwith production credentials. - Monitoring/alerting on webhook delivery failures via the dashboard delivery log.