Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.payments.sardine.ai/llms.txt

Use this file to discover all available pages before exploring further.

Authentication

All Identity API endpoints use HTTP Basic Auth. Pass your clientId as the username and clientSecret as the password on every request.
curl https://api.sandbox.sardine.ai/v1/identity/... \
  -u "$CLIENT_ID:$CLIENT_SECRET"
Never make these calls from a browser or mobile client. Your clientSecret must remain server-side only.

Step 1 — Check if the customer exists

Before creating a new customer, check whether one already exists for the user’s phone number to avoid duplicate records.
curl -X POST https://api.sandbox.sardine.ai/v1/identity/entities/search \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "phoneNumber": "+14155551234" }'
If customerId is returned, the customer already exists — skip to Step 3. If the response is empty, proceed to Step 2.

Step 2 — Create the customer

Register the user with their phone number. The customerId returned here is your durable reference to this user in all subsequent API calls.
curl -X POST https://api.sandbox.sardine.ai/v1/identity/entities \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "phoneNumber": "+14155551234" }'
{
  "customerId": "3f8c1a22-1234-4abc-9def-000000000001",
  "createdAt": "2026-06-01T10:00:00Z"
}
Store customerId against your user record in your own database.

Step 3 — Generate a widget URL

Call this endpoint from your backend to get a hosted widget URL for the user. Choose the right flow and scope for your use case.
curl -X POST https://api.sandbox.sardine.ai/v1/identity/consents/widget \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": "3f8c1a22-1234-4abc-9def-000000000001",
    "successUrl": "https://yourapp.com/kyc/success",
    "manualKycUrl": "https://yourapp.com/kyc/manual",
    "scope": ["profile", "doc_kyc"],
    "flow": "kyc_input"
  }'
{
  "widgetUrl": "https://identity.sardine.ai/?client_token=abc123&consent_id=xyz789&success_url=..."
}

Choosing a flow

FlowWhen to use
kyc_inputNew users, or when you need the user to verify additional scopes not yet on file
kyc_sharingUser has already been verified by another Sardine partner and you want them to consent to share that identity with you

Choosing scopes

ScopeWhat it collects
profileName, date of birth, address, email, phone number
doc_kycGovernment ID scan (front + back) and biometric liveness check
livenessLiveness check only
ssnSocial Security Number (US users, when required)
Pass multiple scopes together: ["profile", "doc_kyc"] is the standard full-KYC combination.

manualKycUrl

Provide a manualKycUrl as a fallback destination if the user cannot be verified automatically (e.g. document scan quality is too low). This is optional but recommended for production.

Step 4 — Redirect the user

Return the widgetUrl to your frontend and redirect the user to it, or embed it in an iframe.
// Server response to your frontend
res.json({ widgetUrl: data.widgetUrl });

// Frontend redirect
window.location.href = widgetUrl;
The widget handles all verification steps. When the user completes (or abandons) the flow, they are redirected to your successUrl.

Step 5 — Retrieve the verified identity

After the user returns to your successUrl, call this endpoint from your backend to retrieve the verified data.
curl https://api.sandbox.sardine.ai/v1/identity/entities/3f8c1a22-1234-4abc-9def-000000000001 \
  -u "$CLIENT_ID:$CLIENT_SECRET"

Response

{
  "profile": {
    "userId": "3f8c1a22-1234-4abc-9def-000000000001",
    "clientId": "your-client-id",
    "consentId": "b1c2d3e4-5678-4abc-9def-000000000002",
    "consentedAt": "2026-06-01T10:30:00Z",
    "revokedAt": null,
    "primaryIdentity": true,
    "fullName": "Jane Smith",
    "dateOfBirth": "1990-06-15",
    "emailAddress": "jane@example.com",
    "phoneNumber": "+14155551234",
    "address": {
      "street": "123 Main St",
      "city": "San Francisco",
      "region": "CA",
      "postalCode": "94105"
    }
  },
  "documentData": {
    "documentType": "DRIVERS_LICENSE",
    "documentNumber": "D1234567",
    "dateOfBirth": "1990-06-15",
    "expiryDate": "2028-06-15",
    "issuingCountry": "US",
    "firstName": "Jane",
    "lastName": "Smith"
  },
  "documentKyc": {
    "front": "<base64-encoded image>",
    "back": "<base64-encoded image>",
    "selfie": "<base64-encoded image>"
  }
}

What’s included per scope

FieldRequires scope
profileprofile
documentDatadoc_kyc
documentKyc (images)doc_kyc
Fields outside the consented scopes are returned as null. If the identity belongs to another client (i.e. kyc_sharing flow), the API enforces consent state:
StateBehaviour
Consent not found400 Consent not found
Consent pending (user has not approved yet)400 Pending user consent
Consent revoked400 Consent has been revoked
Consent active200 with full data

Error reference

StatusMessageResolution
400Phone number is requiredInclude phoneNumber in the request body
400Customer already existsUse the existing customerId from /identity/entities/search
400Customer ID is requiredInclude customerId in the widget request
400Customer not found for customer IDVerify the customerId was created by this client
400Invalid scopeUse one of: profile, doc_kyc, liveness, ssn
400User already consented to this flowThe user has already completed kyc_input for this client
401UnauthorizedCheck that clientId and clientSecret are correct and being sent as Basic Auth

Go to production

  1. Test the full flow end-to-end in sandbox.
  2. Confirm identity data is retrieved correctly after widget completion.
  3. Contact your Sardine integration contact to complete the review.
  4. Swap api.sandbox.sardine.aiapi.sardine.ai and replace with production credentials.