Skip to main content

Customer API

The Customer API is how the backend of a Company works with stored records after verification — reading history, correcting data, driving state changes, and managing assignments. It is available on the Stored tier (there are no records to manage on the session-based tier).

API reference​

The complete Customer API — Customers, Digital Identities, and Assignments — is published as an interactive reference with every endpoint, request and response schema, parameter, and status code, rendered directly from the OpenAPI specification of the service.

Open the Customer API reference

Resources​

ResourceDescription
CustomerThe master record for a person: personal data, address, custom fields, and links to Digital Identities.
Digital IdentityA single verification attempt and its result (read-only except for state transitions).
AssignmentA link between a Customer and something they were issued — see Assignments.

Availability​

This API belongs to the Stored tier. On a tenant whose record storage is switched off there is nothing to manage, and every endpoint here is refused at the gateway with 403. If you need it, ask Innovatrics to enable storage for your tenant.

Base URL and access token​

Requests go through the API gateway, which validates your token, scopes the request to your tenant, and routes it on:

https://<gateway-host>/integrator/v1/...

For the Innovatrics-managed service the gateway host is api.idv.innovatrics.com; a self-hosted deployment uses your own. The interactive reference has a server selector with the same values.

Your access token must carry the audience onboarding-service. The gateway rejects a token without it before the request reaches the service, so a token that works against the Capture & Workflow API is not automatically accepted here.

Rate limit

The gateway allows 200 requests per minute per tenant, alongside a global limit shared across tenants. Exceeding either returns 429 Too Many Requests.

A 429 comes from the gateway rather than from the service, so — unlike every other error on this page — it does not use the standard error envelope. Do not try to parse it as one. Back off and retry.

Common operations​

OperationCall
Read a CustomerGET /v1/customers/{customerId}
List / filter CustomersGET /v1/customers?… (non-PII filters, including external_id)
Search by personal dataPOST /v1/customers/search (PII in the body)
Create a CustomerPOST /v1/customers (supports a supplied external_id, for migrations)
Update fieldsPATCH /v1/customers/{customerId}
Discover the schemaGET /v1/customers/schema
List the identities of a CustomerGET /v1/customers/{customerId}/identities
Read one identityGET /v1/customers/{customerId}/identities/{identityId}
Transition an identity statePOST /v1/customers/{customerId}/identities/{identityId}/transition
Anonymize a Customer (GDPR)POST /v1/customers/{customerId}/anonymize
Anonymize one identityPOST /v1/customers/{customerId}/identities/{identityId}/anonymize
Read the palm record of a CustomerGET /v1/customers/{customerId}/palm-record

Search that includes personal data uses POST … /search with the query in the request body rather than in the URL, to avoid leaking PII into logs and proxies.

Reading an identity directly​

An identity can also be reached without knowing its Customer, which matters for identities that never produced one — a verification that was rejected, or a session on the session-based tier:

OperationCall
Read an identity by its own idGET /v1/identities/{identityId}
Read a captured image of an identityGET /v1/identities/{identityId}/images/{slot}
Anonymize an identity by its own idPOST /v1/identities/{identityId}/anonymize

Assignments​

An Assignment records that a Customer was issued something — a SIM, a device, an account, a policy, a ticket. It is deliberately independent of verification: no workflow guard applies, and a running verification never modifies one.

OperationCall
List by the thing issuedGET /v1/assignments?external_identifier=…
List everything a Customer holdsGET /v1/customers/{customerId}/assignments
CreatePOST /v1/assignments
ReadGET /v1/assignments/{assignmentId}
Update the free-form fieldsPATCH /v1/assignments/{assignmentId}
Suspend / resumePOST /v1/assignments/{assignmentId}/suspend · …/resume
Transfer or unassignPOST /v1/assignments/transfer
Delete outrightDELETE /v1/assignments/{assignmentId}
Discover the schemaGET /v1/assignments/schema

An Assignment is active, suspended or unassigned. The first two are open states and hold the identifier; only one open Assignment may exist for a given external_identifier at a time, so the API can answer "who holds this SIM right now" with a single record.

Three things are worth knowing before you build against it:

  • Move it with transfer, not delete-then-create. POST /v1/assignments/transfer closes the current holder's Assignment and opens the new one in a single transaction. Passing to_customer_id: null unassigns instead, closing the Assignment without opening a replacement. Doing it in two calls risks leaving the identifier unheld, or held twice, if the second call fails.
  • DELETE erases history. It is for correcting a mistaken entry. To record that an assignment legitimately ended, unassign it through transfer — that keeps the record and stamps unassigned_at.
  • Only remote_reference and custom_fields are mutable. The link itself — which Customer, which identifier, which Digital Identity — is fixed once written.

POST /v1/assignments and POST /v1/assignments/transfer accept an Idempotency-Key header. Send one and a retry returns the original outcome instead of acting twice.

Custom fields​

Beyond the typed base fields (name, date of birth, address, and standard identity fields), a Customer carries a free-form custom_fields object for Company-specific extension data. No schema declaration is required to write custom fields; keys a Company wants to search on are registered separately as indexed fields. GET …/schema reports the typed base fields and their flags.

External identifier​

Every Customer has an external_id — the identifier under which your own systems know the person. It is unique within the tenant, limited to 64 characters from a–z A–Z 0–9 . _ -, and by default equals the Customer identifier.

There are three ways to work with it:

  • Compose it in the workflow — the create_new_customer outcome action accepts an external_id_template such as ${document.personal_number}_${uuid}, so newly onboarded Customers follow your existing scheme from day one. See the manifest reference.
  • Supply it on create — when migrating an existing customer base, POST /v1/customers accepts the historical external_id; uniqueness is validated.
  • Query by it — external_id is a filter of the list endpoint and a field of the search body, so your backend can look up a Customer by its own key without storing the Customer identifier of the platform.

The external identifier is shown in the Customer detail and returned in webhook payloads. If your scheme embeds a personal number, treat the identifier as personal data: it is wiped by anonymization like the rest of the record.

Anonymization is terminal​

Anonymization irreversibly erases the personal data, images and biometric templates of a record. It is the mechanism behind a GDPR erasure request, so there is no undo and no hard delete — the record remains as a shell for audit and counting, with the personal data gone.

Anonymizing a Customer changes its identifier

The customer_id you supplied may itself carry personal data — many integrators compose it from a national identification number — so erasing the record also replaces the identifier with a freshly generated opaque one.

The anonymize response returns that new customer_id, and the id you called with stops resolving. Store the returned value if you ever need to reach the erased shell again, for an audit or a regulator request. An erasure job that discards the response loses the only handle on the record.

Once a record is anonymized, further writes to it are refused:

ResponseMeaning
409 CUSTOMER_ANONYMIZEDThe Customer was anonymized. Reads still work; writes and transitions do not.
409 IDENTITY_ANONYMIZEDThe identity was anonymized.

Treat both as final rather than retrying. Anonymizing an already-anonymized record is a no-op, so your erasure job can safely run again.

Writing safely around active verifications​

While a verification workflow is running for a Customer, it is authoritative over that Customer's identity data, and integrator writes that would race it are rejected with 409 WORKFLOW_RUN_ACTIVE. The response identifies the active run. Retry once it completes.

Three groups of writes behave differently:

WriteWhile a workflow is runningIf the guard itself is unreachable
PATCH on a workflow-managed field — name, given_name, surname, date_of_birth, place_of_birth409 WORKFLOW_RUN_ACTIVEFails open — the write proceeds
POST …/transition and every …/anonymize409 WORKFLOW_RUN_ACTIVEFails closed — 503 ACTIVE_RUN_GUARD_UNAVAILABLE
PATCH on integrator-managed fields — custom fields, references, preferencesAlways succeedsUnaffected

This keeps the workflow authoritative over identity data while leaving operational fields writable at any time.

Erasure jobs must handle 503

Because anonymization fails closed, a GDPR erasure request can come back as 503 ACTIVE_RUN_GUARD_UNAVAILABLE rather than succeeding. Treat it as retryable and retry until it succeeds — do not record the erasure as done. Re-running anonymization on an already-anonymized record is a no-op, so retrying is always safe.

Who can write​

The Customer API is one of three writers to the record store, alongside verification workflows and the back-office UI. All writes are authenticated (OAuth2) and audited.

See also​