ONP API Reference
This document describes how to integrate with the Guardline onboarding API. It covers authentication, creating onboarding sessions, declaring linked subjects (the legal representative for kyc_minor at session creation, a company’s legal representatives mid-journey for kyb), resolving session links, reusable QR dispensers, tracking execution lifecycle, receiving webhook notifications, and polling for missed events.
The integration model follows a clear separation of responsibilities: Guardline verifies and decides, the integrator orchestrates. The integrator declares the onboarding via a single API call, distributes the URLs returned in the response (or asks Guardline to deliver them), and receives progress and final-decision notifications via webhook.
Overview
Seção intitulada “Overview”The integration flow involves four participants:
| Participant | Responsibility |
|---|---|
| Integrator backend | Creates sessions, receives webhooks, queries executions |
| Guardline API | Manages sessions, tokens, executions, linked subjects, and notifications |
| Integrator frontend | Renders the link directly or mounts an iframe pointing at it |
| Guardline onboarding | Conducts the verification journey with the end user |
A single POST /api/v1/onboarding/sessions call creates an onboarding for any flow type. The request carries the primary subject’s data plus an optional array of linked subjects. The response carries one URL per subject created. Notification of linked subjects (SMS or WhatsApp) is opt-in per call.
For kyc_minor, the integrator declares both the minor (in primary) and the legal representative (in linked_subjects[]) in the same request. The journey for each is independent: the minor opens their URL, the representative opens theirs, and Guardline orchestrates the consolidation. The integrator decides whether Guardline delivers the representative’s URL via SMS/WhatsApp (notify.linked: true) or whether the integrator handles delivery (notify.linked: false or omitted).
All API calls follow the REST pattern over HTTPS, with JSON payloads and responses in Guardline’s standard envelope format.
Environments and Authentication
Seção intitulada “Environments and Authentication”Environments
Seção intitulada “Environments”| Environment | Base URL | API Key prefix |
|---|---|---|
| Sandbox | https://{instance}.onp.dev.guardline.com.br | gl_test_ |
| Production | https://{instance}.onp.prod.guardline.com.br | gl_live_ |
The {instance} value is provisioned during integrator setup and identifies the dedicated infrastructure. Each instance has its own isolated database, application, and storage.
Authentication
Seção intitulada “Authentication”All integrator-facing API calls require the X-API-Key header with the key corresponding to the environment.
X-API-Key: gl_live_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2Keys are provisioned through the Guardline admin panel. The plaintext token is displayed only at creation time. Internally, Guardline stores the SHA-256 hash of the key.
The prefix is a label chosen when the key is created, not a routing mechanism, and it defaults to live when the environment is not specified. A key issued on a sandbox instance without an explicit environment therefore reads gl_live_ while still being a sandbox-only key. The prefix says nothing about which instance will accept it: a key authenticates only against the instance that issued it.
A credential problem always answers 401, never 400. MISSING_TOKEN when no header was sent, INVALID_API_KEY when the key is unknown, revoked or expired. A 400 means the key was accepted and the request body was rejected, so reissuing the key will not resolve it.
HTTP requests are rejected. HTTPS with TLS 1.2 or higher is required in all environments.
Three surfaces depart from that rule:
| Endpoint | Auth |
|---|---|
POST /api/v1/onboarding/executions/{execution_id}/resend-representative-link | Admin or supervisor JWT from the Guardline backoffice. See Resending the Representative Link. |
POST and GET /api/v1/onboarding/executions/{execution_id}/representatives | API key, JWT, or no credential at all: the KYB journey opens them from a magic link and the path execution_id is the capability. See KYB Legal Representatives. |
POST /api/v1/onboarding/dispensers/{token}/sessions and GET /api/v1/link/{token} | Public. Possession of the token is the capability, and both are rate limited per source IP. |
Idempotency
Seção intitulada “Idempotency”Mutating endpoints accept an optional Idempotency-Key request header (any unique string of at most 255 characters, typically a UUID; a longer one returns 400 INVALID_IDEMPOTENCY_KEY). When present, the server caches the response for 15 minutes; subsequent calls with the same key return the cached response with the Idempotency-Replayed: true response header. Without the header, each call is processed independently.
The cache is keyed by the pair (key, endpoint), so reusing one key across two different endpoints does not collide. One endpoint deliberately ignores the header: POST /onboarding/dispensers/{token}/sessions, where replaying a cached response would hand two people the same journey.
Use this when retrying after a network timeout to avoid duplicate executions. Generate a fresh key for each new logical operation.
Response Format
Seção intitulada “Response Format”All responses follow the standard envelope:
Success:
{ "error": false, "data": {}}Error:
{ "error": true, "message": "descrição do erro", "code": "ERROR_CODE", "request_id": "550e8400-e29b-41d4-a716-446655440000"}The message field is localized. Integrators must branch on the code field (stable, untranslated), not on the message text.
The request_id field is returned in all responses and can be used for tracking in support tickets.
Error Message Locale
Seção intitulada “Error Message Locale”The message field is resolved from the Accept-Language request header. pt-BR is the base locale and is what a caller that sends no header receives.
Accept-Language | Result |
|---|---|
| absent, unparseable, or unmatched | pt-BR (base locale) |
pt-BR | pt-BR |
es-AR | Spanish (Argentina) |
curl https://{instance}.onp.dev.guardline.com.br/api/v1/link/invalido \ -H "Accept-Language: es-AR"{ "error": true, "message": "Enlace no encontrado", "code": "LINK_NOT_FOUND", "request_id": "77440a78-6e11-4def-8c08-31b1189ccbd4"}The catalogue is keyed by error code. A code with no entry in the requested locale degrades to its pt-BR message rather than to a blank or a placeholder, so a response is never empty and the code is always authoritative. This is the reason integrators must never match on message text: the same code yields different strings depending on the header.
Available Journeys
Seção intitulada “Available Journeys”GET /api/v1/onboarding/flowsReturns the list of configured and active journeys for the integrator, with the metadata needed to create sessions correctly.
kyc_representative is filtered server-side and does not appear: that child is created only as a linked_subjects[] entry of a kyc_minor session, and pointing flow_id at one is rejected with INVALID_FLOW_TYPE.
kyb_representative is not filtered and can appear in this list on instances that configure it. Do not start a session against it. It is the subflow a KYB parent provisions for each declared representative (see KYB Legal Representatives), and starting it directly produces an orphan execution with no company to attach to. Select journeys by the type field rather than by position, and treat kyc, kyb and kyc_minor as the startable set.
Response
Seção intitulada “Response”{ "error": false, "data": [ { "flow_id": "61000000-0000-0000-0000-000000000001", "flow_type": "kyc", "name": "KYC Pessoa Física", "description": "Verificação de identidade para maiores de 18 anos.", "status": "active", "steps_count": 8, "steps": [ { "id": "cpf", "name": "CPF", "order": 1 }, { "id": "personal_data", "name": "Dados pessoais", "order": 2 }, { "id": "contact", "name": "Informações de contato", "order": 3 }, { "id": "professional_data", "name": "Dados profissionais", "order": 4 }, { "id": "address", "name": "Endereço", "order": 5 }, { "id": "terms", "name": "Termos de uso", "order": 6 }, { "id": "document", "name": "Documentoscopia", "order": 7 }, { "id": "biometric", "name": "Biometria facial", "order": 8 } ], "primary_schema": [ { "field": "full_name", "type": "string", "required": false, "description": "Nome completo" }, { "field": "tax_id", "type": "string", "required": false, "description": "CPF (somente dígitos ou com máscara)" }, { "field": "email", "type": "string", "required": false, "description": "Endereço de e-mail" }, { "field": "phone", "type": "string", "required": false, "description": "Telefone com código do país" }, { "field": "birth_date", "type": "string", "required": false, "description": "Data de nascimento (AAAA-MM-DD)" } ] }, { "flow_id": "61000000-0000-0000-0000-000000000010", "flow_type": "kyc_minor", "name": "KYC Menor de Idade", "description": "Verificação de identidade para menores entre 14 e 17 anos, com etapa adicional do representante legal.", "status": "active", "steps_count": 9, "steps": [ { "id": "cpf_birth_date", "name": "CPF e data de nascimento", "order": 1 }, { "id": "welcome", "name": "Boas-vindas", "order": 2 }, { "id": "personal_data", "name": "Dados pessoais do menor", "order": 3 }, { "id": "contact", "name": "Informações de contato", "order": 4 }, { "id": "professional_data", "name": "Dados profissionais e PEP", "order": 5 }, { "id": "address", "name": "Endereço", "order": 6 }, { "id": "terms", "name": "Termos de uso", "order": 7 }, { "id": "document", "name": "Documentoscopia do menor", "order": 8 }, { "id": "biometric", "name": "Biometria facial do menor", "order": 9 } ], "primary_schema": [ { "field": "full_name", "type": "string", "required": true, "description": "Nome completo do menor" }, { "field": "tax_id", "type": "string", "required": true, "description": "CPF do menor" }, { "field": "birth_date", "type": "string", "required": true, "description": "Data de nascimento do menor (AAAA-MM-DD), idade entre 14 e 17 anos" }, { "field": "phone", "type": "string", "required": false, "description": "Telefone do menor com código do país" }, { "field": "email", "type": "string", "required": false, "description": "Email do menor" } ] } ]}Each flow entry also returns flow_id (the specific flow version, usable as flow_id in POST /sessions) and steps_count (the number of entries in steps).
The primary_schema array describes the fields accepted under the primary block of POST /api/v1/onboarding/sessions. For kyc_minor, the integrator additionally declares the legal representative under linked_subjects[] (see Linked Subjects). The representative’s journey runs independently from the minor’s; steps above lists only the minor’s steps.
KYB flows additionally return company_schema, the same array shape describing the fields accepted under the company block (see Company Pre-fill (KYB)). It is absent on person flows, which have no company subject. Read the schema rather than hardcoding the field list: it is the server’s own statement of what the flow accepts.
Creating a Session
Seção intitulada “Creating a Session”POST /api/v1/onboarding/sessionsCreates a new onboarding session and returns a URL per subject. For kyc_minor, the response includes a separate URL for the linked representative alongside the minor’s URL. KYB representatives are not declared here: their count and identity are only known mid-journey, so they are declared at the representatives-setup step instead (see KYB Legal Representatives).
Request Schema
Seção intitulada “Request Schema”| Field | Type | Required | Description |
|---|---|---|---|
flow_type | string | One of flow_type or flow_id | Journey type. Possible values: kyc, kyb, kyc_minor. Resolves to the active flow of that type for the integrator. |
flow_id | UUID | One of flow_type or flow_id | Specific flow version. Use this to pin a particular flow configuration when multiple flows of the same type exist. Resolution to a flow whose type is internal-use (such as kyc_representative) is rejected with INVALID_FLOW_TYPE. |
channel | enum | No | Origin of the session: web, sdk, api, asistido_agencia. Default: web. The legacy alias channel_code is also accepted; when both are sent, channel wins. asistido_agencia marks a session opened by a branch officer on the applicant’s behalf and is only meaningful on instances configured for it. |
layout_mode | enum | No | UI layout for the journey: stepper (default) or chat. |
reference_id | string (≤ 255) | No | Free-form correlator for the integrator’s system. Returned in webhooks and queryable via API. Recommended: a unique stable identifier such as the account ID or onboarding request ID. |
parent_execution_id | UUID | No | Links this execution to a parent execution. Reserved for advanced use cases such as KYB sub-flows; not required for standard kyc/kyc_minor/kyb journeys. |
metadata | object | No | Free-form passthrough. Persisted on the execution and exposed back in queries. |
surface_hint | enum | No | Set to embed when the integrator will render the journey inside an iframe. Drives PII sanitization on GET /api/v1/link/{token}. See Surface Hint (Embed Mode). |
redirect_url | string (≤ 2048) | No | Absolute https URL the journey sends the person to after the terminal screen, so the integrator gets its user back into its own application. Rejected with 422 UNPROCESSABLE when the scheme is not https or the host is missing; javascript:, data: and http:// values are refused at creation. Ignored in embed mode, where the embed bridge posts dismissed and the host page owns navigation. With no value the terminal screen offers no exit. |
primary | object | Required for kyc_minor; optional for kyc and kyb | Primary subject’s data. Fields are described by the primary_schema returned in GET /flows. May be omitted (cold-start) for kyc and kyb, in which case the customer record is materialized when the user submits the document step in the journey. primary.tax_id is validated against the flow’s country, see Tax ID by Flow Country. |
company | object | No; KYB only | Company pre-fill for a KYB flow. See Company Pre-fill (KYB). Dropped on person flows: a KYB execution’s subject is a company, and the person and company subjects are mutually exclusive on an execution. |
linked_subjects | array | Required for kyc_minor (exactly one entry, role: "representative"); empty for other flow types | Subjects linked to the primary. See Linked Subjects. |
notify | object | No | Notification opt-in flags. See Notification Opt-in. |
Tax ID by Flow Country
Seção intitulada “Tax ID by Flow Country”primary.tax_id is validated against the country the resolved flow declares, not against the raw flow_type in the request. The country comes from the flow record, so the same API contract serves Brazilian and Argentine instances without a per-tenant switch.
| Flow country | Accepted in primary.tax_id |
|---|---|
BR | A valid CPF (11 digits, check digits verified). |
AR | A CUIT/CUIL of 11 digits with the check digit verified, or a DNI of 7 to 8 digits. Letters are refused. |
| other | The foreign-identifier shape: 4 to 20 characters, letters, digits and hyphens, upper-cased and stripped of spaces, dots and slashes. Never reduced to digits. |
Formatted input is accepted and normalized server-side, so 20-12345678-9 and 20123456789 are the same value. A mismatch returns 400 INVALID_TAX_ID.
Two consequences worth planning for:
- The check runs after the flow is resolved. A request that omits
flow_idand relies onflow_typeresolves to the active flow of that type, which is not necessarily the one the integrator has in mind on an instance carrying more than one. Pinflow_idwhen the instance serves flows of more than one country, otherwise an Argentine document can be validated against a Brazilian flow and rejected. - A flow with no explicit country falls back to
BR, which is the strict side.
Company Pre-fill (KYB)
Seção intitulada “Company Pre-fill (KYB)”On a KYB flow the execution’s subject is a company, so primary has nothing to attach to and is dropped. The company block is the KYB counterpart: it opens the journey with the tax ID the integrator already holds instead of asking the applicant for it again.
| Field | Type | Required | Description |
|---|---|---|---|
company.tax_id | string | No | Company CUIT, with or without the mask. Must belong to a juridical person: AFIP prefixes 30, 33 or 34. Anything else returns 422 CUIT_NOT_JURIDICAL_PERSON rather than materializing a company record for a human being. |
company.razon_social | string (≤ 255) | No | Registered company name, subject to the same free-text whitelist as full_name. Optional: the padron lookup and the statute OCR both fill it later, and neither overwrites a name the integrator supplied. |
{ "flow_id": "61000000-0000-0000-0000-000000000092", "company": { "tax_id": "30712345678", "razon_social": "Estancias y Colonias Arizona S.A." }, "reference_id": "KYB-2026-00042"}A CUIT supplied here is locked for the rest of the journey: the applicant cannot edit it in the company step, and an attempt returns COMPANY_TAX_ID_LOCKED. If the number is wrong, issue a new session rather than trying to correct it in place.
Linked Subjects
Seção intitulada “Linked Subjects”The linked_subjects array declares any subject linked to the primary by the flow contract. Today, this covers the legal representative for kyc_minor. For KYB, the company’s legal representatives are declared mid-journey instead, see KYB Legal Representatives.
Each entry has:
| Field | Type | Required | Description |
|---|---|---|---|
role | enum | Yes | Role discriminator: representative or legal_representative. kyc_minor requires exactly one entry with representative. Other values (such as officer) are reserved and rejected. |
subject | object | Yes | Subject data block. May be empty ({}) for shell creation. |
The subject block accepts the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
full_name | string | Paired with tax_id (see Field Requiredness below) | Full name. |
tax_id | string | Paired with full_name | Personal tax ID, in the shape the flow’s country issues: CPF on BR flows, CUIT/CUIL or DNI on AR flows. See Tax ID by Flow Country. |
phone | string (E.164) | Required if notify.linked is true; optional otherwise | Phone with country code. Validated against the flow’s phone policy: a mobile-only flow rejects landlines with 422 PHONE_POLICY_VIOLATION. |
birth_date | string (YYYY-MM-DD) | No | Date of birth. When provided, it must indicate age 18 or over; a younger representative is rejected with 422 REPRESENTATIVE_AGE_BELOW_MINIMUM. |
email | string | No | Email. |
relationship | enum | Required for kyc_minor; optional otherwise | Relationship to the primary. Values: mother, father, grandparent, legal_guardian, tutor, other. |
notification_channel | enum | No | Preferred channel for backend-managed delivery: sms, whatsapp, or email_sms. |
Field Requiredness for linked_subjects[i].subject
Seção intitulada “Field Requiredness for linked_subjects[i].subject”The service accepts three shapes:
| Shape | Content | Effect |
|---|---|---|
| Shell | subject: {} (or subject without both tax_id and full_name) | Creates the linked execution without materializing a customer record. Integrator delivers the URL manually. |
| Mixed | subject: {tax_id: "..."} or subject: {full_name: "..."} (one of the two only) | Accepted; treated as shell. The single field does not trigger customer materialization. Not an error. |
| Materialized | subject: {tax_id: "...", full_name: "...", ...} (both present) | Creates or reuses a customer record by tax_id and back-links it to the linked execution. Other subject fields (email, phone, birth_date) populate the new customer record on creation; ignored when the customer already exists (see CPF Reuse Semantics). |
Independent of the shape, notify.linked: true requires subject.phone to actually dispatch. Without phone, notify_sent is recorded as false and no provider call is made.
kyc_minor Cardinality
Seção intitulada “kyc_minor Cardinality”For flow_type: kyc_minor, the service requires exactly one entry in linked_subjects with role: "representative". Any of the following returns 400 LINKED_SUBJECTS_REQUIRED:
linked_subjectsfield absentlinked_subjects: [](empty array)- More than one entry
- An entry with
roleother thanrepresentative
Shell creation is explicit: send linked_subjects: [{role: "representative", subject: {}}].
For kyc_minor, two additional gates apply to the representative entry:
linked_subjects[0].subject.relationshipis required. Omitting it returns 400VALIDATION_FAILED.- The representative’s
tax_idmust differ from the minor’stax_id. Reusing the same CPF returns 422CPF_MINOR_REPRESENTATIVE_MATCH.
Notification Opt-in
Seção intitulada “Notification Opt-in”The notify block controls whether Guardline delivers the URL via SMS or WhatsApp.
| Field | Type | Default | Description |
|---|---|---|---|
notify.primary | bool | false | Reserved. Currently has no effect; future versions will dispatch the primary’s URL. |
notify.linked | bool | false | When true, Guardline records the opt-in and dispatches an SMS or WhatsApp message containing the representative’s URL when the minor finishes their module (not at session creation). The opt-in applies to the kyc_minor representative whose subject.phone is present. |
Dispatch Timing
Seção intitulada “Dispatch Timing”For kyc_minor, the representative is only needed after the minor completes their journey. Guardline persists the notify.linked opt-in at POST /sessions and fires the SMS or WhatsApp message at that later moment (when the parent transitions to pending_representative), not synchronously during session creation. As a result, the linked_subjects[i].notify_sent field in the POST /sessions response is always false. The actual dispatch outcome surfaces later via representative_notify_sent on GET /api/v1/link/{token} for the parent’s token (read from the child execution row).
Channel Selection
Seção intitulada “Channel Selection”When the dispatch fires and subject.phone is present, the channel is selected by:
linked_subjects[i].subject.notification_channelif present (sms,whatsapp, oremail_sms).- Otherwise, both channels are attempted in parallel as a fallback.
notify_sent Semantics
Seção intitulada “notify_sent Semantics”The linked_subjects[i].notify_sent field in the POST /sessions response is always false, because the dispatch is deferred to the minor’s completion (see Dispatch Timing). It does not indicate whether a message was sent.
To observe the actual dispatch outcome, read representative_notify_sent from GET /api/v1/link/{token}:
true: at least one attempted channel returned a provider message ID (Twilio MessageSID).false:notify.linkedwas off, the subject’sphonewas absent, or the provider rejected the send.
A provider message ID does not mean the message was delivered to the recipient. Asynchronous delivery confirmation (provider status callbacks) is not surfaced in this API; it remains in communication-log records accessible via the admin panel. This is a deliberate trade-off to keep the API surface honest and small.
Surface Hint (Embed Mode)
Seção intitulada “Surface Hint (Embed Mode)”Integrators rendering the journey inside an iframe set surface_hint: "embed" on the request. The backend persists the flag on the execution. When the iframe loads /api/v1/link/{token} to render the journey, the response is sanitized (CPF, email, phone, birth_date stripped from the customer block; representative URL fields stripped from the parent payload). See PII Sanitization Behavior.
The surface_hint value is server-internal and is not echoed in any response (neither /sessions nor /link/{token}). Integrators observe its effect only via the sanitized shape of the customer block in /link/{token}.
The legacy GET /api/v1/embed/{token} endpoint, which served as a separate sanitized resolver, has been removed. Embed integrators now use /api/v1/link/{token} after creating the session with surface_hint: "embed".
CPF Reuse Semantics
Seção intitulada “CPF Reuse Semantics”When primary.tax_id matches an existing active record in customers_person, the service reuses the existing customer rather than creating a new one. The fields primary.full_name, primary.email, primary.phone, and primary.birth_date from the request are ignored in this case; the customer keeps the data from its first creation.
The same applies to linked_subjects[i].subject.tax_id when the linked subject’s CPF matches an existing record.
Reason: idempotency. Retrying with the same CPF does not duplicate customer records. Trade-off: customer data is sticky to the first creation; corrections via subsequent POST /sessions calls do not propagate.
To update an existing customer’s name, email, or phone, use the backoffice or the customer-management endpoints; POST /sessions is not the path for customer updates. For local development, use a fresh CPF per test or delete the existing record before reusing the CPF.
Examples
Seção intitulada “Examples”Standard KYC, cold-start (no primary data)
Seção intitulada “Standard KYC, cold-start (no primary data)”{ "flow_type": "kyc", "channel": "web", "reference_id": "SOL-2026-00099"}The customer record is materialized when the user submits the document step in the journey. customer_id is absent from the response until then.
Standard KYC, full primary data
Seção intitulada “Standard KYC, full primary data”{ "flow_type": "kyc", "channel": "web", "reference_id": "SOL-2026-00099", "primary": { "full_name": "Carlos Oliveira", "tax_id": "11122233344", "email": "carlos@exemplo.com", "phone": "+5511999990000", "birth_date": "1990-05-15" }}KYC Minor with full data and Guardline-delivered notification
Seção intitulada “KYC Minor with full data and Guardline-delivered notification”{ "flow_type": "kyc_minor", "channel": "web", "reference_id": "SOL-2026-00042", "primary": { "full_name": "João Santos", "tax_id": "12345678900", "birth_date": "2010-05-15" }, "linked_subjects": [ { "role": "representative", "subject": { "full_name": "Maria Santos", "tax_id": "98765432100", "phone": "+5511999998888", "email": "maria@exemplo.com", "birth_date": "1985-03-20", "relationship": "mother", "notification_channel": "sms" } } ], "notify": { "primary": false, "linked": true }}Guardline creates both executions and records the notify.linked opt-in. The SMS to +5511999998888 containing the representative’s URL is dispatched when the minor finishes their module, not at session creation; the POST /sessions response therefore returns notify_sent: false (see Dispatch Timing).
KYC Minor with shell representative (integrator delivers link)
Seção intitulada “KYC Minor with shell representative (integrator delivers link)”{ "flow_type": "kyc_minor", "reference_id": "SOL-2026-00043", "primary": { "full_name": "Ana Pereira", "tax_id": "12399988877", "birth_date": "2009-11-30" }, "linked_subjects": [ { "role": "representative", "subject": {} } ]}The representative’s URL is returned in the response (linked_subjects[0].url). The integrator delivers it through their own channel. No notification is dispatched (notify.linked defaulted to false).
Embed mode (iframe rendering)
Seção intitulada “Embed mode (iframe rendering)”{ "flow_type": "kyc", "surface_hint": "embed", "primary": { "full_name": "Lucas Almeida", "tax_id": "55566677788" }}The session’s surface_hint flag drives PII sanitization on GET /api/v1/link/{token} when the iframe resolves the token.
Success Response (201)
Seção intitulada “Success Response (201)”{ "error": false, "data": { "execution_id": "9b4771d5-ab60-4b5e-867d-573ed7cb684c", "customer_id": "155384e8-c86b-478e-be99-3937d2fee1ab", "url": "https://acme.onp.prod.guardline.com.br/onboarding/link/abc123def456", "embed_url": "https://acme.onp.prod.guardline.com.br/embed/t/abc123def456", "token": "<token>", "expires_at": "2026-05-13T12:49:34Z", "status": "created", "reference_id": "SOL-2026-00042", "linked_subjects": [ { "role": "representative", "execution_id": "c0aef12b-7c2a-4b3e-9a8d-f1e2d3c4b5a6", "url": "https://acme.onp.prod.guardline.com.br/onboarding/link/def456ghi789", "embed_url": "https://acme.onp.prod.guardline.com.br/embed/t/def456ghi789", "subject_status": "created", "notify_sent": false } ] }}| Field | Description |
|---|---|
execution_id | Unique execution identifier for the primary subject. |
customer_id | Customer record identifier. Absent from the response in cold-start (no primary data sent), because no customer record exists yet; present and stable once the record exists. Test for the presence of the key, not against the zero UUID: earlier builds published 00000000-0000-0000-0000-000000000000 here and integrators read it as an identifier that would be filled in later, which is why the field is now elided instead. |
url | Public URL for the primary subject’s journey. |
embed_url | URL pointing to the iframe-friendly route for the primary subject. Use this as the iframe src when rendering inside the integrator’s app. |
token | Slug of the link, also accepted by GET /api/v1/link/{token}. |
expires_at | Link expiration in ISO 8601. Default: 7 days after creation, configurable per flow. |
status | Initial execution status (created). |
reference_id | Echo of the reference_id sent in the request, when provided. |
linked_subjects[] | One entry per linked subject created. Always present (empty array for non-kyc_minor flows). |
linked_subjects[i].role | Role of the linked subject (representative for kyc_minor). |
linked_subjects[i].execution_id | Linked subject’s execution ID. |
linked_subjects[i].url | Linked subject’s journey URL. |
linked_subjects[i].embed_url | Iframe-friendly URL for the linked subject. |
linked_subjects[i].subject_status | Linked subject’s initial status (created). |
linked_subjects[i].notify_sent | Always false in this response. Representative dispatch is deferred to the minor’s completion, so the live outcome is read later from representative_notify_sent on GET /api/v1/link/{token} (see Dispatch Timing). |
The url field is intended for direct user access via SMS, WhatsApp, email, or any other channel the integrator chooses. The embed_url field is intended for iframe and webview usage. Both point to the same execution and resolve to the same content.
Note: error
messagefields are localized byAccept-Language. Match oncodefor programmatic branching, not on message text.
| HTTP | Code | Cause |
|---|---|---|
| 400 | INVALID_REQUEST | Malformed JSON, unknown field (the message names the field), or wrong field type. |
| 400 | VALIDATION_FAILED | Field-level validation failed: neither flow_id nor flow_type was sent, flow_id is not a UUID, flow_id and flow_type disagree, the resolved flow is inactive, flow_type not in the enum, channel outside the enum, relationship or notification_channel outside the enum, missing relationship on the kyc_minor representative, kyc_minor birth_date outside the 14-17 range, or invalid linked_subjects[i].role. The envelope does not name which of these fired; correlate with the request_id. |
| 400 | INVALID_TAX_ID | primary.tax_id does not match the shape the resolved flow’s country issues. See Tax ID by Flow Country. |
| 400 | LINKED_SUBJECTS_REQUIRED | kyc_minor request with missing, empty, or oversized linked_subjects array, or with a role other than representative. |
| 400 | INVALID_FLOW_TYPE | The flow_type provided does not exist or is not active for the integrator, or flow_id resolves to an internal-use flow type (such as kyc_representative). |
| 400 | MISSING_REQUIRED_FIELDS | kyc_minor request without primary.tax_id or without primary.full_name. |
| 401 | MISSING_API_KEY | API Key header not provided. |
| 401 | INVALID_API_KEY | API Key invalid, revoked, or expired. |
| 404 | FLOW_NOT_FOUND | The flow_id does not exist. |
| 409 | ACTIVE_SESSION_EXISTS | An active session already exists for the same primary subject and flow. |
| 422 | PHONE_POLICY_VIOLATION | A mobile-only flow received a landline number in primary.phone or linked_subjects[i].subject.phone. |
| 422 | CPF_MINOR_REPRESENTATIVE_MATCH | kyc_minor request where the representative’s tax_id equals the minor’s tax_id. |
| 422 | REPRESENTATIVE_AGE_BELOW_MINIMUM | The representative’s birth_date indicates age under 18. |
| 422 | CUIT_NOT_JURIDICAL_PERSON | company.tax_id is not a juridical-person CUIT (prefix 30, 33 or 34). |
| 422 | UNPROCESSABLE | redirect_url is not an absolute https URL with a host, or exceeds 2048 characters. |
| 429 | RATE_LIMITED | Request limit exceeded. Respect the Retry-After header. |
| 502 | ONBOARDING_ERROR | Internal error during session creation. Retry after a short delay; persistent failures should be reported to support with the request_id. |
Resolving the Link
Seção intitulada “Resolving the Link”GET /api/v1/link/{token}Public endpoint (no authentication). The integrator’s frontend, the iframe, or any browser opening the url from the session response calls this endpoint to load the journey. Returns the flow definition, steps, and the customer block.
When the session was created with surface_hint: "embed", the response is sanitized (see PII Sanitization Behavior).
Rate limit: 10 requests per hour per source IP, shared across the entire /api/v1/link/* namespace (not per token). Integrators resolving multiple links from the same office IP in sequence consume the same bucket.
Response (Default)
Seção intitulada “Response (Default)”{ "error": false, "data": { "execution_id": "9b4771d5-ab60-4b5e-867d-573ed7cb684c", "flow": { "id": "61000000-0000-0000-0000-000000000010", "type": "kyc_minor", "name": "KYC Menor", "layout_mode": "stepper", "cpf_match_mode": "block", "phone_policy": "mobile-only", "settings": {}, "steps": [ { "id": "...", "execution_step_id": "...", "step_type_code": "welcome", "step_type_name": "Boas-vindas", "step_order": 1, "title": "Boas-vindas", "config": {}, "is_required": true, "is_enabled": true, "status": "pending" } ] }, "customer": { "id": "155384e8-c86b-478e-be99-3937d2fee1ab", "name": "João Santos", "cpf": "12345678900", "cpf_masked": "***456789**", "email": "joao@exemplo.com", "phone": "+5511999887766", "birth_date": "2010-05-15" }, "minor": null, "relationship": "mother", "status": "in_progress", "expires_at": "2026-05-13T12:49:34Z", "allowed_origins": ["https://cliente.com"], "representative_link": "https://acme.onp.prod.guardline.com.br/onboarding/link/def456ghi789", "representative_token": "<linked-token>", "representative_status": "pending", "representative_notify_sent": true }}| Field | Description |
|---|---|
execution_id | Execution identifier of the subject this token belongs to. |
flow | Flow definition: id, type, name, layout_mode, cpf_match_mode (alert or block), phone_policy (any or mobile-only), settings, and the ordered steps. Each step carries id, execution_step_id, step_type_code, step_type_name, step_order, title, description, config, is_required, is_enabled, status, and (default surface only) display_data and completed_fields. |
customer | Customer record. Fields below. |
customer.id | UUID of the customer record. Absent when the execution is in cold-start. |
customer.name | Full name. |
customer.cpf | Unmasked CPF. Removed when surface_hint=embed. |
customer.cpf_masked | Masked CPF (always present when cpf is present). |
customer.email | Email. Removed when surface_hint=embed. |
customer.phone | Phone in E.164. Removed when surface_hint=embed. |
customer.birth_date | Date of birth in YYYY-MM-DD. Removed when surface_hint=embed. |
minor | When this token belongs to the representative of a kyc_minor pair, carries the minor’s customer block (same shape as customer); null otherwise. Removed when surface_hint=embed. |
relationship | Representative’s relationship to the minor (mother, father, etc.). Present only on kyc_minor tokens; omitted otherwise. |
status | Current execution status. See Execution Lifecycle. |
expires_at | Link expiration. |
allowed_origins | Origins allowed to embed the iframe (CSP frame-ancestors). |
representative_link | Present only when this token belongs to the primary subject of a kyc_minor pair. URL of the linked representative. Removed when surface_hint=embed. |
representative_token | Linked representative’s token (FE uses for redirection). Removed when surface_hint=embed. |
representative_status | Current status of the representative’s execution (pending, in_progress, etc.). Preserved under surface_hint=embed so the FE knows whether to poll. |
representative_notify_sent | true when the representative dispatch (fired on minor completion, see Dispatch Timing) returned a provider message ID; false when notify.linked was off or phone was absent; absent when the flow has no representative. Removed when surface_hint=embed. |
redirect_url | Echo of the redirect_url the session was created with, so the journey knows where to send the person after the terminal screen. Absent when the session declared none, in which case the terminal screen offers no exit. Re-validated on read: a stored value that no longer meets the absolute-https policy is returned as absent rather than honored. |
company | Company bound to this execution, so a KYB journey can open with the CUIT the integrator sent instead of asking for it again. Carries id, name (razon social, may be empty) and tax_id (CUIT, digits only, returned unmasked). Absent on person journeys and on a KYB execution whose company is not materialized yet. Preserved when surface_hint=embed, see below. |
beneficial_owners_snapshot | The company’s beneficial-owner declaration (owners, DDJJ, razon social), seeded onto a representative child at declare time so the representative’s KYC can render and confirm it. Absent on non-representative journeys. |
PII Sanitization Behavior
Seção intitulada “PII Sanitization Behavior”When the session was created with surface_hint: "embed", the resolver applies in-process sanitization before returning the payload:
- Removed:
customer.cpf,customer.email,customer.phone,customer.birth_date,minor(set tonull),representative_link,representative_token,representative_notify_sent, allflow.steps[].display_dataandflow.steps[].completed_fields. - Preserved:
customer.id,customer.name,customer.cpf_masked, the entireflowblock (id, type, name, layout_mode, cpf_match_mode, phone_policy, settings, steps),relationship,status,expires_at,allowed_origins,representative_status,execution_id,company,redirect_url,beneficial_owners_snapshot.
company is preserved deliberately, and the asymmetry with customer.cpf is only apparent. What the rule removes is the identity document of a natural person, which is what customer.cpf carries whatever the local journey calls it. A CUIT is a company’s fiscal id and is public in the ARCA padron that the same journey queries by CUIT, so blanking it would buy no privacy and would break the pre-fill it exists for. The two also cannot coexist: an execution’s subject is either a person or a company, never both, so on a KYB journey the customer block is empty anyway.
Sessions created without surface_hint (default) receive the full payload.
Note: error
messagefields are localized byAccept-Language. See Error Message Locale.
| HTTP | Code | Cause |
|---|---|---|
| 404 | LINK_NOT_FOUND | Token does not exist. |
| 410 | LINK_EXPIRED | Token expired. |
| 429 | RATE_LIMITED | 10 requests per hour per source IP exceeded, shared across all tokens. |
Resending the Representative Link
Seção intitulada “Resending the Representative Link”POST /api/v1/onboarding/executions/{execution_id}/resend-representative-linkRe-fires the SMS or WhatsApp dispatch for the representative of an existing kyc_minor execution. Does not create a new representative execution; it re-uses the existing linked child and re-attempts notification.
Authentication
Seção intitulada “Authentication”This endpoint requires an admin or supervisor JWT obtained via the Guardline backoffice. The X-API-Key header alone is not sufficient. Calls without a valid JWT return 401 MISSING_TOKEN.
The endpoint is intended for administrative recovery flows (an integrator’s support team detects the representative did not receive the link, the operator clicks “resend” in the backoffice). It is not designed to be called from integrator-facing automation. For integrator-driven dispatch, set notify.linked: true on the original POST /sessions and (if the link expires) create a new session.
Idempotency
Seção intitulada “Idempotency”The endpoint honors the Idempotency-Key header; see Idempotency.
A maximum of 3 resends per execution. The 4th attempt returns 429 RATE_LIMITED.
| HTTP | Code | Cause |
|---|---|---|
| 401 | MISSING_TOKEN | JWT not provided. |
| 403 | FORBIDDEN | JWT does not have admin or supervisor role. |
| 404 | EXECUTION_NOT_FOUND | The primary execution does not exist. |
| 404 | NO_RESENDABLE_CHILD | No representative child in a non-terminal state to resend. |
| 409 | EXECUTION_TERMINATED | Primary execution is in a terminal state. |
| 429 | RATE_LIMITED | 3-resend cap reached. |
KYB Legal Representatives
Seção intitulada “KYB Legal Representatives”A KYB journey does not know upfront how many people can legally bind the company. The count and identity are declared mid-journey, at the representatives-setup step, once the company subject is established. Each declared representative becomes its own KYC subflow with its own link, so the KYB parent and every representative progress independently.
This generalizes the single-representative pattern of kyc_minor to 1..N, and it is why KYB representatives are not declared in linked_subjects[] at session creation.
Declaring Representatives
Seção intitulada “Declaring Representatives”POST /api/v1/onboarding/executions/{execution_id}/representatives{execution_id} is the KYB parent execution. Honors Idempotency-Key.
| Field | Type | Required | Description |
|---|---|---|---|
representatives | array (1 to 20) | Yes | One entry per legal representative. Each entry uses the same subject block as linked_subjects[i].subject: full_name, tax_id, email, phone, birth_date, relationship, notification_channel. |
notify | object | No | Notification opt-in flags, same semantics as on POST /sessions. |
{ "representatives": [ { "full_name": "María Gómez", "tax_id": "27123456784", "phone": "+543794123456" }, { "full_name": "Juan Pérez", "tax_id": "20123456786", "email": "juan@example.com" } ], "notify": { "linked": true }}Response (200):
{ "error": false, "data": { "representatives": [ { "role": "legal_representative", "execution_id": "c0aef12b-7c2a-4b3e-9a8d-f1e2d3c4b5a6", "url": "https://acme.onp.prod.guardline.com.br/onboarding/link/def456ghi789", "embed_url": "https://acme.onp.prod.guardline.com.br/embed/t/def456ghi789", "link_token": "<link_token>", "expires_at": "2026-05-20T12:49:34Z", "notify_sent": true } ] }}Each entry carries the representative’s own child execution and a fresh link. url resumes that representative’s KYC subflow and is what the integrator shares when it handles delivery itself; notify_sent reports whether Guardline dispatched the link, and requires phone on the entry.
Polling Representative Progress
Seção intitulada “Polling Representative Progress”GET /api/v1/onboarding/executions/{execution_id}/representativesReturns the declared children with their live status, so the integrator (or the journey) can render per-representative progress without opening each link.
{ "error": false, "data": { "representatives": [ { "execution_id": "c0aef12b-7c2a-4b3e-9a8d-f1e2d3c4b5a6", "status_code": "in_progress", "full_name": "María Gómez", "link_token": "<link_token>", "expires_at": "2026-05-20T12:49:34Z", "notify_sent": true } ] }}Authentication and Limits
Seção intitulada “Authentication and Limits”Both endpoints accept an API key or a JWT, and also answer without either: the KYB journey opens them from a magic link that carries no credential, and the path execution_id is the capability. The handler validates the execution, and both are rate limited to 60 requests per minute per source IP, sized for the progress poll (roughly 6 per minute per journey).
| HTTP | Code | Cause |
|---|---|---|
| 400 | INVALID_ID | execution_id is not a valid UUID. |
| 400 | INVALID_REQUEST | Malformed JSON or wrong field type. |
| 400 | VALIDATION_FAILED | Empty representatives, more than 20 entries, or a field-level failure inside an entry. |
| 404 | EXECUTION_NOT_FOUND | The parent execution does not exist. |
| 429 | RATE_LIMITED | 60/minute per IP exceeded. |
| 502 | INTERNAL_ERROR | Internal error while provisioning or listing the children. |
Reusable QR Dispenser
Seção intitulada “Reusable QR Dispenser”A dispenser is a stable token behind a QR code that mints a fresh onboarding session on every scan. It is the answer to the branch and event use case, where one printed code serves many different people, and it exists because a normal session link is single-subject: reusing it would put a second person into the first person’s execution.
Only self-service flow types are eligible. kyc and kyb can have a dispenser; kyc_minor and kyc_representative cannot, because those journeys are provisioned as a declared pair rather than opened by whoever walks up.
Creating or Fetching the Dispenser
Seção intitulada “Creating or Fetching the Dispenser”POST /api/v1/onboarding/dispensersRequires an API key or JWT. Idempotent by design: one active dispenser per flow, so calling it again returns the existing one rather than minting a second.
{ "flow_id": "61000000-0000-0000-0000-000000000090" }Response (200):
{ "error": false, "data": { "dispenser_token": "<dispenser_token>", "flow_id": "61000000-0000-0000-0000-000000000090", "url": "https://acme.onp.prod.guardline.com.br/onboarding/d/kQ8xR2mN7pL4" }}url is the value to render as a QR code. It is a scan target, not a journey link.
| HTTP | Code | Cause |
|---|---|---|
| 400 | VALIDATION_FAILED | flow_id absent or not a UUID. |
| 404 | FLOW_NOT_FOUND | The flow does not exist. |
| 422 | INVALID_FLOW_TYPE | The flow type does not support a reusable dispenser (kyc_minor, kyc_representative). |
Dispensing a Session
Seção intitulada “Dispensing a Session”POST /api/v1/onboarding/dispensers/{token}/sessionsPublic, called once per scan by the person’s own browser. Possession of the dispenser token is the capability, so no API key or JWT is sent. Rate limited to 30 requests per minute per source IP.
{ "error": false, "data": { "url": "https://acme.onp.prod.guardline.com.br/onboarding/link/abc123def456", "token": "<token>", "expires_at": "2026-05-20T12:49:34Z" }}Each call creates a new cold-start session, so every scan yields a different link and a different execution. This endpoint deliberately does not honor Idempotency-Key: replaying a cached response would hand two people the same journey, which is exactly what the dispenser exists to prevent.
| HTTP | Code | Cause |
|---|---|---|
| 400 | MISSING_TOKEN | Dispenser token absent from the path. |
| 404 | LINK_NOT_FOUND | Dispenser does not exist or is inactive. |
| 429 | RATE_LIMITED | 30/minute per IP exceeded. |
Execution Lifecycle
Seção intitulada “Execution Lifecycle”State Diagram
Seção intitulada “State Diagram”State Descriptions
Seção intitulada “State Descriptions”| State | Terminal | Description |
|---|---|---|
created | No | Session created, user has not yet accessed the link. |
in_progress | No | User is navigating through journey steps. |
pending_representative | No | Minor’s journey finished, waiting for the legal representative. Applies only to kyc_minor. |
processing | No | All steps submitted; decision engine processing. |
pending_review | No | Referred for manual review. Resolution SLA: 72 hours. |
approved | Yes | Approved by the engine or by an analyst. |
rejected | Yes | Rejected by the engine or by an analyst. |
blocked | Yes | Journey interrupted by a security rule (CPF restriction, biometric attempt limit). |
cancelled | Yes | Cancelled by the integrator or by an administrator. |
expired | Yes | Session expired before completion. |
Linkage Sub-state for kyc_minor
Seção intitulada “Linkage Sub-state for kyc_minor”For kyc_minor, the parent execution holds the lifecycle state above, while the representative’s progress is tracked separately on the linked child execution. GET /api/v1/link/{token} on the parent’s token surfaces the representative’s status via the representative_status field.
The parent transitions to pending_representative when the minor completes their journey. While the representative is doing their part, the parent stays in pending_representative; the child execution moves through created → in_progress → processing → terminal independently. When the child reaches a terminal positive state, the parent’s consolidation runs, and the parent moves through processing to its own terminal state.
Querying an Execution
Seção intitulada “Querying an Execution”GET /api/v1/onboarding/executions/{execution_id}Returns the complete state of an execution, including data collected during the journey. This endpoint is the primary data source for the integrator. Webhooks notify state transitions; details are obtained through this query.
Standard KYC Response (200)
Seção intitulada “Standard KYC Response (200)”{ "error": false, "data": { "execution_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "flow_type": "kyc", "reference_id": "SOL-2026-00099", "status": "approved", "decision": { "result": "approved", "risk_level": "low", "decided_at": "2026-03-26T15:30:00Z", "decided_by": "engine" }, "customer": { "full_name": "Carlos Oliveira", "tax_id": "11122233344", "tax_id_masked": "111.***.**3-44", "birth_date": "1990-05-15", "nationality": "Brasileira", "email": "carlos@exemplo.com", "phone": "+5511999990000", "income": "8500.00", "is_pep": false, "has_foreign_tax_obligation": false, "address": { "zip_code": "01310-100", "street": "Avenida Paulista", "number": "1000", "complement": "Sala 302", "neighborhood": "Bela Vista", "city": "São Paulo", "state": "SP" }, "document": { "type": "cnh", "status": "verified" }, "biometric": { "status": "verified" } }, "data_divergences": [], "steps_completed": 8, "steps_total": 8, "created_at": "2026-03-26T14:00:00Z", "started_at": "2026-03-26T14:05:00Z", "completed_at": "2026-03-26T14:12:00Z", "expires_at": "2026-04-02T14:00:00Z" }}| Field | Description |
|---|---|
execution_id | Execution identifier. |
status | Current execution status. See Execution Lifecycle. |
flow_type | Journey type. |
reference_id | Echo of the integrator’s correlator, when the session declared one. |
parent_execution_id | Names the parent on a linked child execution (a kyb_representative, or a kyc_minor representative), so a representative’s journey can be correlated back to the onboarding that spawned it without keeping a local map. Absent on root executions. |
steps_completed / steps_total | Journey progress. |
decision | Decision block: result, risk_level, decided_by, decided_at. Absent until a decision exists. |
customer | Person subject of the execution. Fields below. Absent on KYB executions. |
company | Company subject, on KYB executions: legal_name, tax_id (raw, no formatting), trade_name, tax_category, company_type. An execution’s subject is a person or a company, never both, so this block and customer are mutually exclusive. List rows carry only legal_name and tax_id; the single-execution read adds the rest. |
device | Device-risk projection of the journey. Fields below. Single-execution read only: the list endpoint omits it on purpose, since filling it costs a payload read and parse per item on every page. |
data_divergences | Always present as an array, empty when there is none. Each entry reports a field whose value submitted during the journey diverged from the pre-filled value: field, expected_masked, submitted_masked, step_id, recorded_at. Values are masked at insertion; raw values never leave the backend. |
created_at / started_at / completed_at | Journey timestamps. |
expires_at | The execution deadline, derived from the flow’s expires_in_hours. |
link_expires_at | The separate window the one-time link stays openable for. |
expires_at and link_expires_at are both returned because neither alone says when the journey dies. The expiration worker retires an execution on whichever of three clocks fires first: link_expires_at while still created, expires_at while created or in_progress, and the session-inactivity window once in_progress. Returning only expires_at made integrators read the 7-day link figure from POST /sessions and the shorter execution figure here under the same name, with no way to tell which governs.
customer block
Seção intitulada “customer block”| Field | Description |
|---|---|
full_name | Full name as collected. |
given_name / surname | The discriminated halves of full_name, resolved from the columns the localized name step writes, then that step’s payload, then a split of full_name. full_name is always sent alongside so a consumer can recompose the exact value when a compound given name splits differently than expected. |
tax_id | The person’s identity document, raw and unformatted. On Brazilian journeys this is the CPF. On localized journeys it is whatever the journey collects as the identity document: the DNI, the cédula or the passport. |
tax_number | The person’s fiscal id, on Argentine journeys: the CUIL, or the CUIT when they declared one. A deliberately separate field from tax_id, because moving the fiscal id into tax_id would silently change the meaning of a field integrators already read as the identity document. Collected by the document step, so it is empty on Brazilian journeys and on any localized flow that does not collect it. Treat it as optional. This is the field to key a user profile by on an Argentine integration. |
tax_id_masked | Masked variant, kept for backward compatibility. Soft-deprecated: prefer tax_id. New integrations should ignore it; existing ones keep receiving it unchanged. |
birth_date, nationality, email, phone, income | As collected. phone carries the dial prefix whenever the source does, so a number collected by a localized journey reads as E.164 rather than as national digits with no country. |
is_pep, has_foreign_tax_obligation | Always emitted, including when false. |
address | zip_code, street, number, complement, neighborhood, city, state. |
document, biometric | Verification status blocks: type and status. |
representative | Representative data nested inside customer, carrying the same shape plus relationship, guardianship_document and status. Reserved for backward compatibility; see the note on kyc_minor below. |
device block
Seção intitulada “device block”Present when the journey has a readable device profile. Deliberately narrower than the analyst-facing profile: IP forensics (address, ISP, city, coordinates, ASN, postal code) and browser fingerprint detail stay behind the authenticated internal surfaces rather than riding an outbound call. ip_country is the one geographic field kept, because country granularity is what the anti-fraud reading needs and it does not locate a person.
| Field | Description |
|---|---|
provider, device_id, advice | Source provider, its device identifier, and its recommendation. |
risk_score, risk_label | Provider risk score and band. |
is_rooted, is_emulator, is_vpn, is_proxy, is_tor, is_bot, is_remote_access, is_incognito | Always emitted, including when false, so a receiver can distinguish false from absent. Absence is carried by the whole device block being omitted. |
device_type, os, os_version, ip_country | Device and origin metadata. |
signals | Provider signal names behind the flags above (for example Jailbreak, Root hiding application), so a receiver wanting the evidence for a flag does not have to call back. Names only. |
KYC Minor Response (200)
Seção intitulada “KYC Minor Response (200)”For kyc_minor, the parent execution’s customer block describes the minor. The representative’s data is fetched by querying the linked child execution separately. Embedded representative data inside the parent response is reserved for backwards-compatible cases and may be deprecated in a future version; new integrations should query the linked child execution explicitly using the execution_id returned in linked_subjects[].execution_id from POST /sessions.
The relationship field on the representative’s customer record accepts: mother, father, grandparent, legal_guardian, tutor, other. When the relationship is anything other than mother or father, the representative’s journey requires uploading a guardianship document; the upload status is reflected in guardianship_document (null or "uploaded").
The decision block is only populated when the execution reaches a terminal state (approved, rejected) or is under review (pending_review). In all other states the key is absent from the response, not present as null. The same holds for every optional block on this payload (customer, company, device, parent_execution_id, address, document, biometric): test for the presence of the key. The one deliberate exception is guardianship_document, which is emitted as an explicit null when no document was uploaded, and data_divergences, which is always present as an array.
| HTTP | Code | Cause |
|---|---|---|
| 401 | MISSING_API_KEY | API Key header not provided. |
| 401 | INVALID_API_KEY | API Key invalid, revoked, or expired. |
| 404 | EXECUTION_NOT_FOUND | The execution_id does not exist or does not belong to the integrator. |
Active Executions
Seção intitulada “Active Executions”GET /api/v1/onboarding/executionsReturns active executions with valid sessions. Executions in a terminal state (approved, rejected, blocked, expired, cancelled) are not included in the response.
Query Parameters
Seção intitulada “Query Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Requested page. |
per_page | integer | 20 | Items per page (maximum: 100). |
status | string | (none) | Filter by execution status. When omitted, terminal states (approved, rejected, blocked, expired, cancelled) are excluded; passing an explicit status includes it. |
reference_id | string (≤ 255) | (none) | Exact match on the integrator’s reference_id. As an exact-key lookup it returns the row in any status: the default terminal-status exclusion does not apply, so this is the way to fetch an execution you already know the id of without having to guess its status. |
flow_id | UUID | (none) | Filter by a specific flow version. |
name | string (≤ 255) | (none) | Case-insensitive substring match on the customer’s name. |
tax_id | string | (none) | Filter by CPF (11 digits) or CNPJ (14 digits). |
Response (200)
Seção intitulada “Response (200)”{ "error": false, "data": { "items": [ { "execution_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "flow_type": "kyc_minor", "reference_id": "SOL-2026-00042", "status": "in_progress", "steps_completed": 5, "steps_total": 9, "created_at": "2026-03-31T09:00:00Z", "started_at": "2026-03-31T09:03:00Z", "expires_at": "2026-04-07T09:00:00Z", "customer": { "full_name": "João Santos", "tax_id": "12345678900", "tax_id_masked": "123.***.**9-00", "email": "joao@exemplo.com" } } ], "pagination": { "page": 1, "per_page": 20, "total": 1, "total_pages": 1 } }}Pagination metadata is nested under data.pagination (page, per_page, total, total_pages). Each item carries summary fields plus a customer block (full_name, tax_id, tax_id_masked, email); the block is null when no customer is linked yet (cold-start). The decision block is detail-only: fetch it via the individual query endpoint.
Cancelling an Execution
Seção intitulada “Cancelling an Execution”POST /api/v1/executions/{execution_id}/cancelTransitions the execution to the cancelled state. When the execution has child executions (the representative under a kyc_minor parent), the call cascades and cancels them in the same operation.
Because the partial unique index idx_executions_active_person_flow excludes terminal statuses, cancelling frees the (customer_person_id, flow_id) slot. After a successful cancel, a new POST /onboarding/sessions for the same CPF and flow stops returning 409 ACTIVE_SESSION_EXISTS and a fresh session can be created. Typical use cases: reopening a kyc_minor journey for retesting, recovering from a wrong submission, or rotating a session that the end user could not finish in time.
A onboarding.cancelled webhook event is emitted on success; see Event Families.
Authentication
Seção intitulada “Authentication”Accepts either:
X-API-Key(gl_live_*orgl_test_*), orAuthorization: Bearer <JWT>(backoffice JWT).
Idempotency
Seção intitulada “Idempotency”Honors the Idempotency-Key header; see Idempotency.
Request Body
Seção intitulada “Request Body”The body is optional. The only accepted field is reason, recorded in the audit trail and in the onboarding.cancelled event payload:
{ "reason": "Duplicate registration, recreating with correct data"}| Field | Type | Required | Description |
|---|---|---|---|
reason | string (max 500) | No | Free-text reason recorded in audit and event payload. |
Success Response (200)
Seção intitulada “Success Response (200)”{ "error": false, "data": { "execution_id": "dc8fc63f-f25a-4ce7-a9a7-2005bb3f1689", "cancelled_children": [ "b11d2b44-3a8e-4a0f-9c4d-90d1f8a2e111" ], "status_code": "cancelled", "cancelled_at": "2026-05-27T13:50:00Z" }}| Field | Type | Description |
|---|---|---|
execution_id | UUID | Execution that was cancelled. |
cancelled_children | UUID array | UUIDs of child executions cancelled in cascade (the representative under a kyc_minor). Empty when the execution has no children. |
status_code | string | Always cancelled on success. |
cancelled_at | ISO 8601 | Cancellation timestamp. |
| HTTP | Code | Cause |
|---|---|---|
| 400 | INVALID_ID | The path UUID is malformed. |
| 400 | INVALID_REQUEST | The request body is present but malformed. |
| 401 | MISSING_TOKEN / INVALID_API_KEY | Authentication missing, invalid, or expired. |
| 404 | NOT_FOUND | No execution with the given UUID. |
| 409 | EXECUTION_TERMINAL | Execution already in a terminal state (approved, rejected, cancelled, expired, blocked). |
| 429 | RATE_LIMITED | Rate limit exceeded. |
Webhooks
Seção intitulada “Webhooks”Webhooks are HTTP notifications sent by Guardline to the integrator’s backend when an execution changes state. Payloads are intentionally lean: they contain the event identifiers and a small data block. Complete data should be obtained via the query endpoint.
Event Families
Seção intitulada “Event Families”Two families coexist post-rewrite:
onboarding.*: state of the onboarding as a whole. Emitted on every execution.linkage.*: state of a linked subject within a paired flow. This covers the legal representative forkyc_minorand the legal representatives declared on akybparent. Emitted on the parent execution, with the linked subject’s outcome reflected in the payload.
onboarding.* Events
Seção intitulada “onboarding.* Events”| Event | Description |
|---|---|
onboarding.started | Session created via POST /sessions. |
onboarding.completed | All steps submitted; decision engine processing. |
onboarding.approved | Approved by the engine or by an analyst. |
onboarding.rejected | Rejected by the engine or by an analyst. |
onboarding.review | Referred for manual review. |
onboarding.blocked | Journey interrupted by a security rule. |
onboarding.expired | Session expired before completion. |
onboarding.cancelled | Cancelled by the integrator or by an administrator. |
onboarding.documents_requested | An analyst reopened specific steps and reissued the link. See below. |
onboarding.documents_requested
Seção intitulada “onboarding.documents_requested”The only event that asks the integrator to act rather than merely reporting an outcome. It fires when an analyst in the backoffice reopens specific steps of an execution under review, which rotates the link and restarts its clock. The payload carries what the integrator needs to send the customer back without querying anything:
{ "event": "onboarding.documents_requested", "execution_id": "9b4771d5-ab60-4b5e-867d-573ed7cb684c", "flow_type": "kyc", "reference_id": "SOL-2026-00042", "timestamp": "2026-05-13T12:49:34Z", "data": { "reason": "Foto do documento ilegível", "requested_steps": ["document-front", "document-back"], "resume_url": "https://acme.onp.prod.guardline.com.br/onboarding/link/<token>", "link_expires_at": "2026-05-20T12:49:34Z" }}resume_url travels in the payload because the link was rotated: the URL the integrator already held is no longer the one to send. reference_id falls back to the execution id when the session was created without one, so the field is never an empty string.
linkage.* Events (kyc_minor)
Seção intitulada “linkage.* Events (kyc_minor)”| Event | Description |
|---|---|
linkage.pending | The minor completed their journey and the parent transitioned to pending_representative. The representative’s URL was created at POST /sessions; this event marks the parent’s transition, not the child’s creation. |
linkage.started | The legal representative accessed their link and submitted the first step. |
linkage.completed | The legal representative completed their journey; the parent’s consolidation has been triggered. |
linkage.rejected | An analyst rejected the representative’s execution in manual review. |
linkage.expired | The representative’s execution expired before completion. |
Legacy representative.* Events
Seção intitulada “Legacy representative.* Events”The integrator API rewrite replaced representative.{pending, started, completed} with the linkage.* family above. The legacy event types are not emitted on the new code paths, but historical webhook deliveries in integrator-side audit logs may reference them. Treat both taxonomies as informationally equivalent for historical data; only linkage.* is emitted today.
Emission Order
Seção intitulada “Emission Order”Events follow a strict emission order within each execution. Due to retries, events may arrive out of order at the integrator’s endpoint. The event_sequence field (monotonically increasing per execution) should be used as the canonical ordering criterion, not the timestamp or delivery order.
Standard KYC
Seção intitulada “Standard KYC”Automatic approval:
onboarding.started (1) session createdonboarding.completed (2) all steps submittedonboarding.approved (3) approved by engineRejection:
onboarding.started (1)onboarding.completed (2)onboarding.rejected (3)Manual review, then resolved:
onboarding.started (1)onboarding.completed (2)onboarding.review (3)onboarding.approved (4) analyst decision (or onboarding.rejected)Blocked:
onboarding.started (1)onboarding.blocked (2)Expired:
onboarding.started (1)onboarding.expired (2)KYC Minor
Seção intitulada “KYC Minor”All events on a kyc_minor execution reference the parent (minor’s) execution_id. The representative’s progress surfaces via linkage.* events with the same execution_id.
Automatic approval:
onboarding.started (1) session createdlinkage.pending (2) minor completed; parent in pending_representativelinkage.started (3) representative accessed their linklinkage.completed (4) representative finishedonboarding.completed (5) consolidation; decision engine processingonboarding.approved (6) approved by engineRejection:
onboarding.started (1)linkage.pending (2)linkage.started (3)linkage.completed (4)onboarding.completed (5)onboarding.rejected (6)Manual review, then resolved:
onboarding.started (1)linkage.pending (2)linkage.started (3)linkage.completed (4)onboarding.completed (5)onboarding.review (6)onboarding.approved (7) analyst decision (or onboarding.rejected)Blocked during the minor module:
onboarding.started (1)onboarding.blocked (2)Expired waiting for the representative:
onboarding.started (1)linkage.pending (2)linkage.expired (3) representative's link expiredonboarding.expired (4) parent expired as a consequenceRepresentative rejected by analyst:
onboarding.started (1)linkage.pending (2)linkage.started (3)linkage.completed (4)onboarding.completed (5)onboarding.review (6)linkage.rejected (7) analyst rejected the representativeonboarding.rejected (8) parent decision propagatesPayload
Seção intitulada “Payload”{ "event": "onboarding.approved", "execution_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "flow_type": "kyc_minor", "reference_id": "SOL-2026-00042", "event_sequence": 6, "timestamp": "2026-04-01T10:45:00Z"}| Field | Description |
|---|---|
event | Event type. |
execution_id | Parent execution identifier. For linkage.* events on kyc_minor, this is the minor’s execution ID; the linked child’s identifier is included inside the payload data block. |
flow_type | Journey type. |
reference_id | Integrator identifier, as sent during session creation. |
event_sequence | Sequential event number within the execution, for canonical ordering. |
timestamp | Event emission timestamp in ISO 8601. |
data | Additive block, absent when it would be empty. Contents below. |
The data block
Seção intitulada “The data block”Everything here is additive: a key is simply absent when there is nothing to report, so a receiver written against an older envelope keeps working.
| Key | When present | Contents |
|---|---|---|
data.decision | Once the execution has a decision | result, risk_level, decided_by, decided_at. |
data.device | On every event except onboarding.started, when the journey has a readable device profile | The same device-risk block as the execution read (see device block). onboarding.started is skipped on purpose: it fires at session creation, before the browser profiler has run, so the read is guaranteed empty. |
data.company | On kyb parent events | tax_id, legal_name, and trade_name when known. |
data.legal_representatives | On kyb parent events | One entry per declared representative: execution_id, status, flag (true when the representative is rejected or expired, so a consumer can spot the blocking one without evaluating statuses itself), plus reference_id, full_name, name, surname and document when the representative’s record carries them. |
The KYB shape is deliberate: a company event carries the company identity and all its representatives in one payload, so the integrator receives the whole company at once instead of correlating N per-execution events. Only the kyb parent emits it; the children are kyb_representative executions and emit their own ordinary events.
For onboarding.documents_requested the data block carries a different, event-specific shape, documented with that event.
Headers
Seção intitulada “Headers”| Header | Description |
|---|---|
Content-Type | Always application/json. |
X-Guardline-Event-ID | Stable event UUID, identical across retries. Use for deduplication. |
X-Guardline-Delivery-ID | Unique UUID per delivery attempt. Changes on each retry. |
X-Guardline-Attempt-Number | Sequential attempt number (1 for first delivery, 2 for first retry, etc.). |
X-Guardline-Timestamp | Unix timestamp of the signature moment (when HMAC is enabled). |
X-Guardline-Signature | HMAC-SHA256 signature in hexadecimal (when HMAC is enabled). |
Authorization | Bearer token (when OAuth is enabled). |
X-API-Key | Static key the integrator’s endpoint expects, when one is configured on the webhook endpoint. Optional and independent of the HMAC signature. |
Webhook Authentication
Seção intitulada “Webhook Authentication”Guardline supports three authentication modes for webhook delivery. The mode is configured per webhook endpoint in the admin panel. HMAC signature is always present regardless of the selected mode, providing message integrity verification on every delivery.
| Mode | HMAC Signature | OAuth Bearer | Client Certificate |
|---|---|---|---|
| HMAC only (default) | Yes | No | No |
| OAuth 2.0 | Yes | Yes | No |
| mTLS | Yes | No | Yes |
| OAuth 2.0 + mTLS | Yes | Yes | Yes |
HMAC-SHA256 Authentication
Seção intitulada “HMAC-SHA256 Authentication”HMAC authentication uses a shared secret provisioned during integrator setup in the admin panel.
Verification process on the receiver:
- Extract the
X-Guardline-Timestampheader. - Reject if the timestamp is older than 5 minutes relative to the server clock.
- Concatenate timestamp and request body:
{timestamp}.{body}. - Compute
HMAC-SHA256of the concatenation using the shared secret. - Encode the result in hexadecimal.
- Compare with the
X-Guardline-Signatureheader using constant-time comparison. - Reject if there is a mismatch.
Pseudocode:
import hmac, hashlib, time
def verify_webhook(body: bytes, timestamp: str, signature: str, secret: str) -> bool: if abs(time.time() - int(timestamp)) > 300: return False
message = f"{timestamp}.".encode() + body expected = hmac.new( secret.encode(), message, hashlib.sha256 ).hexdigest()
return hmac.compare_digest(expected, signature)mTLS Authentication
Seção intitulada “mTLS Authentication”Mutual TLS authentication ensures Guardline’s identity at the transport layer, without requiring application-level validation.
Setup:
- During provisioning, Guardline provides the root CA certificate (
guardline-ca.pem). - The integrator configures their webhook server to require client certificates.
- The integrator adds
guardline-ca.pemto the server’s trusted CA list. - On each delivery, Guardline presents a client certificate signed by the root CA.
- The integrator’s server validates the certificate chain automatically at the TLS layer.
The CN field of the client certificate contains the integrator’s instance identifier. The integrator can optionally validate this field to ensure the request comes from the correct instance.
When mTLS is enabled, the X-Guardline-Signature and X-Guardline-Timestamp headers are not sent.
OAuth 2.0 Authentication
Seção intitulada “OAuth 2.0 Authentication”OAuth authentication allows Guardline to obtain an access token before each delivery, using the Client Credentials flow (RFC 6749, section 4.4).
Setup:
- The integrator provides in the admin panel:
token_endpoint,client_id,client_secret, and optionallyscope. - Before each delivery, Guardline requests a token from the provided endpoint.
- The token request follows the standard format:
POST {token_endpoint}Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id={client_id}&client_secret={client_secret}&scope={scope}- Guardline includes the obtained token in the webhook authorization header:
Authorization: Bearer {access_token}- The integrator validates the token at their webhook endpoint according to their OAuth implementation.
Guardline caches the token respecting the expires_in field from the token endpoint response, with a 60-second safety margin. If the webhook receiver returns 401, the cached token is invalidated and a fresh token is acquired for a single retry.
If token acquisition fails due to invalid credentials (401 or 400 from the token endpoint), the delivery is immediately marked as exhausted and is not retried. Transient token endpoint failures (500, timeout) follow the normal retry policy.
Endpoint Configuration
Seção intitulada “Endpoint Configuration”The webhook endpoint is configured in the Guardline admin panel. Requirements:
| Requirement | Detail |
|---|---|
| Protocol | HTTPS required. |
| TLS version | 1.2 or higher. |
| Success response | Any 2xx code (body ignored). |
| Timeout | Respond within 10 seconds. |
| Idempotency | Deduplicate deliveries using X-Guardline-Event-ID. |
Implementation recommendation: receive the webhook, enqueue for asynchronous processing, and respond immediately with 200. This avoids timeouts and ensures reliable delivery.
Credential Rotation
Seção intitulada “Credential Rotation”When OAuth credentials or mTLS certificates are rotated, in-flight retries for previously dispatched deliveries continue using the credentials that were active at the time of dispatch. New deliveries use the updated credentials immediately.
If failed deliveries need to be re-sent with the new credentials, a manual resend from the admin panel creates a new delivery using the latest configured credentials, preserving the original event payload. The original delivery is kept intact for audit purposes.
Retry Policy
Seção intitulada “Retry Policy”When the integrator’s endpoint returns a non-2xx code or does not respond within 10 seconds, Guardline resends the notification with exponential backoff:
| Attempt | Interval after failure |
|---|---|
| 1 | 5 seconds |
| 2 | 30 seconds |
| 3 | 2 minutes |
| 4 | 10 minutes |
| 5 | 30 minutes |
| 6 | 1 hour |
| 7 | 2 hours |
| 8 | 4 hours |
| 9 | 8 hours |
| 10 | 12 hours |
After 10 failed attempts, the delivery is marked as exhausted and an internal alert is generated. Notifications remain accessible via the polling endpoint (see Notification Polling). Manual resend is also available in the admin panel.
Notification Polling
Seção intitulada “Notification Polling”The polling endpoint is the fallback mechanism to ensure no notification is lost, regardless of webhook failures. It can also be used as the primary integration mechanism by integrators who prefer a pull model.
Querying Pending Notifications
Seção intitulada “Querying Pending Notifications”GET /api/v1/onboarding/notificationsThis endpoint uses cursor-based pagination.
| Parameter | Type | Default | Description |
|---|---|---|---|
status | string | pending | Filter by state: pending, failed, retrying. Comma-separated for multiple values. |
limit | integer | 50 | Items per page (1 to 200; values outside the range fall back to 50). |
after_cursor | string | (none) | Cursor for the next page: the base64-encoded delivery_id returned in next_cursor by the previous call. |
Response (200):
{ "error": false, "data": { "items": [ { "delivery_id": "d4e5f6a7-b8c9-0123-def0-123456789abc", "execution_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "event": "onboarding.approved", "flow_type": "kyc_minor", "reference_id": "SOL-2026-00042", "event_sequence": 6, "status": "pending", "attempts": 3, "last_attempt_at": "2026-04-01T10:48:00Z", "timestamp": "2026-04-01T10:45:00Z" } ], "next_cursor": null }}next_cursor is a base64 string when more pages may exist (returned when the page is full), or null when there are no further items. Pass it back as after_cursor to fetch the next page. Each item carries flow_type and reference_id, the current attempts count, the timestamp of the delivery, and last_attempt_at (the most recent attempt; omitted before the first attempt).
Acknowledging Processing
Seção intitulada “Acknowledging Processing”POST /api/v1/onboarding/notifications/{delivery_id}/ackMarks a notification as processed. After acknowledgment, the notification no longer appears in queries with pending status. The operation is idempotent: repeated calls for the same delivery_id return success with no side effects.
Response (200):
{ "error": false, "data": { "delivery_id": "d4e5f6a7-b8c9-0123-def0-123456789abc", "status": "acknowledged" }}Complete Flow Diagrams
Seção intitulada “Complete Flow Diagrams”Standard KYC
Seção intitulada “Standard KYC”KYC Minor with Guardline-Delivered Notification
Seção intitulada “KYC Minor with Guardline-Delivered Notification”Error Reference
Seção intitulada “Error Reference”HTTP Status Codes
Seção intitulada “HTTP Status Codes”| Code | Meaning | Guidance |
|---|---|---|
| 200 | Success | Process normally. |
| 201 | Resource created | Process normally. |
| 400 | Invalid request | Fix the payload. Do not retry. |
| 401 | Authentication failed | Check the API Key (or JWT for resend-representative-link). Do not retry. |
| 404 | Resource not found | Check the identifiers. Do not retry. |
| 409 | Conflict | Handle according to the specific error code. |
| 410 | Gone | The resource was valid but is no longer accessible (e.g., expired link). |
| 429 | Rate limited | Wait for the interval indicated in the Retry-After header. |
| 500 | Internal error | Retry with exponential backoff (maximum 3 attempts). |
| 502 | Internal error during processing | Retry with exponential backoff. |
Domain Error Codes
Seção intitulada “Domain Error Codes”Note: error
messagefields are localized byAccept-Language(see Error Message Locale). Match oncodefor programmatic branching.
| Code | HTTP | Description |
|---|---|---|
MISSING_API_KEY | 401 | API Key header not provided. |
INVALID_API_KEY | 401 | API Key invalid, revoked, or expired. |
MISSING_TOKEN | 401 | JWT not provided (resend-representative-link), or dispenser token absent from the path (400 on that route). |
FORBIDDEN | 403 | JWT lacks the required role (admin or supervisor). |
INVALID_REQUEST | 400 | Malformed JSON, unknown field, or wrong field type. |
INVALID_ID | 400 | A path UUID is malformed. |
VALIDATION_FAILED | 400 | Field-level validation failed, including a request that sends neither flow_id nor flow_type. |
INVALID_TAX_ID | 400 | primary.tax_id does not match the shape the resolved flow’s country issues. |
INVALID_IDEMPOTENCY_KEY | 400 | Idempotency-Key header longer than 255 characters. |
LINKED_SUBJECTS_REQUIRED | 400 | kyc_minor request with missing/empty/oversized linked_subjects or wrong role. |
INVALID_FLOW_TYPE | 400 / 422 | 400 when flow_id resolves to an internal-use flow type; 422 when a flow type does not support a reusable QR dispenser. |
MISSING_REQUIRED_FIELDS | 400 | kyc_minor missing primary.tax_id or primary.full_name. |
PHONE_POLICY_VIOLATION | 422 | A mobile-only flow received a landline phone number. |
CUIT_NOT_JURIDICAL_PERSON | 422 | company.tax_id is not a juridical-person CUIT (prefix 30, 33 or 34). |
COMPANY_TAX_ID_LOCKED | 409 | The company CUIT was supplied at session creation and cannot be edited inside the journey. |
UNPROCESSABLE | 422 | A URL policy was violated, such as a redirect_url that is not an absolute https URL. |
CPF_MINOR_REPRESENTATIVE_MATCH | 422 | The representative’s CPF equals the minor’s CPF. |
REPRESENTATIVE_AGE_BELOW_MINIMUM | 422 | The representative’s birth_date indicates age under 18. |
ACTIVE_SESSION_EXISTS | 409 | Active session exists for the same primary subject and flow. |
EXECUTION_NOT_FOUND | 404 | Execution not found or does not belong to the integrator. |
EXECUTION_TERMINAL | 409 | A cancel was requested on an execution already in a terminal state. |
EXECUTION_TERMINATED | 409 | The primary execution has reached a terminal state (resend-representative-link). |
NO_RESENDABLE_CHILD | 404 | No representative child to resend (resend-representative-link only). |
LINK_NOT_FOUND | 404 | Link token does not exist. |
LINK_EXPIRED | 410 | Link token expired. |
FLOW_NOT_FOUND | 404 | Flow not found. |
RATE_LIMITED | 429 | Request limit exceeded. |
ONBOARDING_ERROR | 502 | Internal error during session creation. |
INTERNAL_ERROR | 502 | Internal error on the KYB representatives endpoints. |
Endpoint Summary
Seção intitulada “Endpoint Summary”| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/v1/onboarding/flows | API Key | List available journeys. |
| POST | /api/v1/onboarding/sessions | API Key | Create an onboarding session (with optional linked subjects, company pre-fill, and return URL). |
| GET | /api/v1/link/{token} | Public (rate-limited) | Resolve a link token to flow + customer payload. Sanitized when the session was created with surface_hint=embed. |
| GET | /api/v1/onboarding/executions/{execution_id} | API Key | Query execution with complete data. |
| GET | /api/v1/onboarding/executions | API Key | List active executions. |
| POST | /api/v1/onboarding/executions/{execution_id}/representatives | API Key, JWT, or journey link | Declare a KYB company’s 1..N legal representatives and mint their KYC links. |
| GET | /api/v1/onboarding/executions/{execution_id}/representatives | API Key, JWT, or journey link | Poll the declared representatives’ live KYC status. |
| POST | /api/v1/onboarding/executions/{execution_id}/resend-representative-link | Admin/Supervisor JWT | Resend the representative link via SMS/WhatsApp. |
| POST | /api/v1/executions/{execution_id}/cancel | API Key | Cancel an execution (and its representative child for kyc_minor), releasing the active-session slot for reuse. |
| POST | /api/v1/onboarding/dispensers | API Key | Create or fetch a flow’s reusable QR dispenser. |
| POST | /api/v1/onboarding/dispensers/{token}/sessions | Public (rate-limited) | Mint a fresh session from a QR scan. |
| GET | /api/v1/onboarding/notifications | API Key | Poll pending webhook notifications. |
| POST | /api/v1/onboarding/notifications/{delivery_id}/ack | API Key | Acknowledge a webhook notification. |