Skip to content

ONP API Reference

This document describes how to integrate with the Guardline onboarding API. It covers authentication, creating onboarding sessions, declaring linked subjects (legal representative for kyc_minor, officers for kyb), resolving session links, 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.

The integration flow involves four participants:

ParticipantResponsibility
Integrator backendCreates sessions, receives webhooks, queries executions
Guardline APIManages sessions, tokens, executions, linked subjects, and notifications
Integrator frontendRenders the link directly or mounts an iframe pointing at it
Guardline onboardingConducts 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.

EnvironmentBase URLAPI Key prefix
Sandboxhttps://{instance}.onp.dev.guardline.com.brgl_test_
Productionhttps://{instance}.onp.prod.guardline.com.brgl_live_

The {instance} value is provisioned during integrator setup and identifies the dedicated infrastructure. Each instance has its own isolated database, application, and storage.

All integrator-facing API calls require the X-API-Key header with the key corresponding to the environment.

X-API-Key: gl_live_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2

Keys 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.

HTTP requests are rejected. HTTPS with TLS 1.2 or higher is required in all environments.

The POST /api/v1/onboarding/executions/{execution_id}/resend-representative-link endpoint is the only exception: it requires an admin or supervisor JWT obtained via the Guardline backoffice. See Resending the Representative Link.

Mutating endpoints accept an optional Idempotency-Key request header (any unique string, typically a UUID). 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.

Use this when retrying after a network timeout to avoid duplicate executions. Generate a fresh key for each new logical operation.

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 in pt-BR. 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.

GET /api/v1/onboarding/flows

Returns the list of configured and active journeys for the integrator, with the metadata needed to create sessions correctly. Internal-use flow types (such as kyc_representative) are filtered server-side and do not appear in the response.

{
"error": false,
"data": [
{
"flow_id": "8a4c2d1e-9f37-4b6a-851d-c3e80f9b2a47",
"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": "b7d3e5c1-2f48-4a90-86b4-d125e7a93f6c",
"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" }
]
}
]
}

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.

POST /api/v1/onboarding/sessions

Creates a new onboarding session and returns a URL per subject. For flows with linked subjects (kyc_minor today, kyb officers in the future), the response includes a separate URL for each linked subject, alongside the primary’s URL.

FieldTypeRequiredDescription
flow_typestringOne of flow_type or flow_idJourney type. Possible values: kyc, kyb, kyc_minor. Resolves to the active flow of that type for the integrator.
flow_idUUIDOne of flow_type or flow_idSpecific 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.
channelenumNoOrigin of the session: web, sdk, api. Default: web. The legacy alias channel_code is also accepted.
layout_modeenumNoUI layout for the journey: stepper (default) or chat.
biometricbooleanNoToggles the facial proof-of-life step for THIS session only, overriding the journey configuration: false suppresses the step (the user advances directly to the next one), true enables it on a journey that ships it disabled, omitted keeps the journey configuration. Requires the resolved flow to carry the biometric step (400 otherwise, in both directions). Applies to the primary execution only; linked-subject journeys keep their flow configuration. The terminal webhook reflects the choice in data.liveness.status (see [data.liveness](#dataliveness)).
reference_idstring (≤ 255)NoFree-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_idUUIDNoLinks 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.
metadataobjectNoFree-form passthrough. Persisted on the execution and exposed back in queries.
surface_hintenumNoSet 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).
primaryobjectRequired for kyc_minor; optional for kyc and kybPrimary 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 CPF/CNPJ step in the journey.
linked_subjectsarrayRequired for kyc_minor (exactly one entry, role: "representative"); empty for other flow typesSubjects linked to the primary. See Linked Subjects.
notifyobjectNoNotification opt-in flags. See Notification Opt-in.

The linked_subjects array declares any subject linked to the primary by the flow contract. Today, this covers the legal representative for kyc_minor. KYB officers and other roles are reserved for future flow types.

Each entry has:

FieldTypeRequiredDescription
roleenumYesRole discriminator. Today only representative is accepted. Other values (such as officer) are reserved and rejected.
subjectobjectYesSubject data block. May be empty ({}) for shell creation.

The subject block accepts the following fields:

FieldTypeRequiredDescription
full_namestringPaired with tax_id (see Field Requiredness below)Full name.
tax_idstringPaired with full_nameCPF (11 digits) or CNPJ (14 digits).
phonestring (E.164)Required if notify.linked is true; optional otherwisePhone with country code.
birth_datestring (YYYY-MM-DD)NoDate of birth. Pass-through; no creation-time validation.
emailstringNoEmail.
relationshipenumYes for kyc_minor; optional otherwiseRepresentative’s relationship with the minor. Required at session creation for kyc_minor regardless of whether the rest of subject is a shell. Values: mother, father, grandparent, legal_guardian, tutor, other. See [kyc_minor Cardinality](#kyc_minor-cardinality).
notification_channelenumNoPreferred channel for backend-managed delivery: sms or whatsapp.

The service accepts three shapes:

ShapeContentEffect
Shellsubject: {} (or subject without both tax_id and full_name)Creates the linked execution without materializing a customer record. Integrator delivers the URL manually.
Mixedsubject: {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.
Materializedsubject: {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.

For flow_type: kyc_minor, subject.relationship is required at session creation regardless of which shape above applies. A shell (subject: {}) without relationship is rejected; see [kyc_minor Cardinality](#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_subjects field absent
  • linked_subjects: [] (empty array)
  • More than one entry
  • An entry with role other than representative

The entry must also carry subject.relationship. Missing subject.relationship returns 422 LINKED_SUBJECT_RELATIONSHIP_REQUIRED. The relationship is the canonical source of the guardian-type value consumed downstream by the bond-document gate and the auto-skip of the in-flow relationship step, so it has to be declared up front even when the rest of the subject is a shell.

Shell creation is explicit and still possible; just include relationship: send linked_subjects: [{role: "representative", subject: {relationship: "mother"}}].

The notify block controls whether Guardline delivers the URL via SMS or WhatsApp.

FieldTypeDefaultDescription
notify.primaryboolfalseReserved. Currently has no effect; future versions will dispatch the primary’s URL.
notify.linkedboolfalseWhen true, Guardline dispatches an SMS or WhatsApp message containing the URL to each linked_subjects[i] whose subject.phone is present.

When notify.linked: true and subject.phone is present, the dispatch channel is selected by:

  1. linked_subjects[i].subject.notification_channel if present (sms or whatsapp).
  2. Otherwise, both channels are attempted in parallel as a fallback.

The response field linked_subjects[i].notify_sent reflects synchronous provider acceptance only:

  • true: at least one attempted channel returned a provider message ID (Twilio MessageSID).
  • false: notify.linked was off, the subject’s phone was absent, or the provider rejected synchronously.

notify_sent: true does not mean the message was delivered to the recipient. Asynchronous delivery confirmation (provider status callbacks) is not surfaced today; 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.

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".

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.

{
"flow_type": "kyc",
"channel": "web",
"reference_id": "SOL-2026-00099"
}

The customer record is materialized when the user submits the CPF step in the journey. customer_id in the response is the zero UUID until then.

{
"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, delivers an SMS to +5511999998888 containing the representative’s URL, and reports the synchronous dispatch outcome in the response.

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": { "relationship": "mother" } }
]
}

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). The relationship field is mandatory for kyc_minor even in shell mode; see [kyc_minor Cardinality](#kyc_minor-cardinality).

{
"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.

{
"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": true
}
]
}
}
FieldDescription
execution_idUnique execution identifier for the primary subject.
customer_idCustomer record identifier. In cold-start (no primary data sent), returns the zero UUID 00000000-0000-0000-0000-000000000000; populated with the real UUID when the CPF/CNPJ step is submitted in the journey. Test against the zero UUID, not against null or empty.
urlPublic URL for the primary subject’s journey.
embed_urlURL pointing to the iframe-friendly route for the primary subject. Use this as the iframe src when rendering inside the integrator’s app.
tokenSlug of the link, also accepted by GET /api/v1/link/{token}.
expires_atLink expiration in ISO 8601. Default: 7 days after creation, configurable per flow.
statusInitial execution status (created).
reference_idEcho 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].roleRole of the linked subject (representative for kyc_minor).
linked_subjects[i].execution_idLinked subject’s execution ID.
linked_subjects[i].urlLinked subject’s journey URL.
linked_subjects[i].embed_urlIframe-friendly URL for the linked subject.
linked_subjects[i].subject_statusLinked subject’s initial status (created).
linked_subjects[i].notify_senttrue if at least one channel was accepted by the provider; false when notify.linked: false, subject.phone absent, or the provider rejected synchronously.

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 message fields are localized in pt-BR. Match on code for programmatic branching, not on message text.

HTTPCodeCause
400INVALID_REQUESTMalformed JSON, unknown field (the message names the field), or wrong field type.
400VALIDATION_FAILEDField-level validation failed: invalid tax_id format, flow_type not in the enum, relationship or notification_channel outside the enum, kyc_minor birth_date outside the 14-17 range, or invalid linked_subjects[i].role.
400LINKED_SUBJECTS_REQUIREDkyc_minor request with missing, empty, or oversized linked_subjects array, or with a role other than representative.
400INVALID_FLOW_TYPEThe 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).
400MISSING_REQUIRED_FIELDSkyc_minor request without primary.tax_id or without primary.full_name.
422LINKED_SUBJECT_RELATIONSHIP_REQUIREDkyc_minor request whose linked_subjects[0].subject.relationship is absent or empty. Applies to all kyc_minor shapes (shell, mixed, materialized).
401MISSING_API_KEYAPI Key header not provided.
401INVALID_API_KEYAPI Key invalid, revoked, or expired.
404FLOW_NOT_FOUNDThe flow_id does not exist.
409ACTIVE_SESSION_EXISTSAn active session already exists for the same primary subject and flow.
429RATE_LIMITEDRequest limit exceeded. Respect the Retry-After header.
502ONBOARDING_ERRORInternal error during session creation. Retry after a short delay; persistent failures should be reported to support with the request_id.
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.

{
"error": false,
"data": {
"execution_id": "9b4771d5-ab60-4b5e-867d-573ed7cb684c",
"flow": {
"id": "61000000-0000-0000-0000-000000000010",
"type": "kyc_minor",
"name": "KYC Menor",
"steps": [
{ "id": "...", "step_type_code": "welcome", "step_order": 1, "title": "Boas-vindas", "config": {} }
]
},
"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"
},
"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
}
}
FieldDescription
execution_idExecution identifier of the subject this token belongs to.
flowFlow definition: id, type, name, ordered steps.
customerCustomer record. Fields below.
customer.idUUID of the customer record. Absent when the execution is in cold-start.
customer.nameFull name.
customer.cpfUnmasked CPF. Removed when surface_hint=embed.
customer.cpf_maskedMasked CPF (always present when cpf is present).
customer.emailEmail. Removed when surface_hint=embed.
customer.phonePhone in E.164. Removed when surface_hint=embed.
customer.birth_dateDate of birth in YYYY-MM-DD. Removed when surface_hint=embed.
statusCurrent execution status. See Execution Lifecycle.
expires_atLink expiration.
allowed_originsOrigins allowed to embed the iframe (CSP frame-ancestors).
representative_linkPresent 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_tokenLinked representative’s token (FE uses for redirection). Removed when surface_hint=embed.
representative_statusCurrent status of the representative’s execution (pending, in_progress, etc.). Preserved under surface_hint=embed so the FE knows whether to poll.
representative_notify_senttrue when Guardline-managed dispatch returned a provider message ID at session creation; false when notify.linked was off or phone was absent; absent when the flow has no representative. Removed when surface_hint=embed.

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, representative_link, representative_token, representative_notify_sent, all flow.steps[].display_data and flow.steps[].completed_fields.
  • Preserved: customer.id, customer.name, customer.cpf_masked, the entire flow block (id, type, name, layout_mode, settings), status, expires_at, allowed_origins, representative_status, execution_id.

Sessions created without surface_hint (default) receive the full payload.

Note: error message fields are localized in pt-BR.

HTTPCodeCause
404LINK_NOT_FOUNDToken does not exist.
410LINK_EXPIREDToken expired.
429RATE_LIMITED10 requests per hour per source IP exceeded, shared across all tokens.
POST /api/v1/onboarding/executions/{execution_id}/resend-representative-link

Re-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.

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.

The endpoint honors the Idempotency-Key header; see Idempotency.

A maximum of 3 resends per execution. The 4th attempt returns 429 RATE_LIMITED.

HTTPCodeCause
401MISSING_TOKENJWT not provided.
403INSUFFICIENT_PERMISSIONSJWT does not have admin or supervisor role.
404EXECUTION_NOT_FOUNDThe primary execution does not exist.
404NO_RESENDABLE_CHILDNo representative child in a non-terminal state to resend.
409EXECUTION_TERMINATEDPrimary execution is in a terminal state.
429RATE_LIMITED3-resend cap reached.
StateTerminalDescription
createdNoSession created, user has not yet accessed the link.
in_progressNoUser is navigating through journey steps.
pending_representativeNoMinor’s journey finished, waiting for the legal representative. Applies only to kyc_minor.
processingNoAll steps submitted; decision engine processing.
pending_reviewNoReferred for manual review. Resolution SLA: 72 hours.
approvedYesApproved by the engine or by an analyst.
rejectedYesRejected by the engine or by an analyst.
blockedYesJourney interrupted by a security rule (CPF restriction, biometric attempt limit).
cancelledYesCancelled by the integrator or by an administrator.
expiredYesSession expired before completion.

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 createdin_progressprocessing → 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.

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.

{
"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",
"reasons": []
},
"customer": {
"full_name": "Carlos Oliveira",
"tax_id": "11132345344",
"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"
}
},
"links": {
"url": "https://acme.onp.prod.guardline.com.br/onboarding/link/abc123def456",
"embed_url": "https://acme.onp.prod.guardline.com.br/embed/t/abc123def456"
},
"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"
}
}

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 field is null.

The decision.reasons array carries the canonical reason(s) behind the decision. It is always present as an array: empty ([]) for automatic engine decisions and for approvals, and populated only when a manual decision (analyst review) recorded a catalog reason. Each entry has a machine-readable code and a human-readable description:

"decision": {
"result": "rejected",
"risk_level": "high",
"decided_at": "2026-03-26T15:30:00Z",
"decided_by": "analyst",
"reasons": [
{ "code": "pep_restriction", "description": "Restrição PEP" }
]
}

Possible code values:

codedescription
invalid_documentDocumento inválido
fraud_suspectedSuspeita de fraude
pep_restrictionRestrição PEP
sanctions_matchMatch em sanções
biometric_failedBiometria reprovada
high_riskRisco elevado
underageMenor de idade
incomplete_docsDocumentação incompleta
expired_documentDocumento vencido
data_inconsistencyDados inconsistentes
otherOutro

Treat code as the stable identifier for programmatic handling. The description mirrors the catalog label and may be tailored per tenant; the catalog can also be extended over time, so consumers should tolerate unknown codes gracefully.

HTTPCodeCause
401MISSING_API_KEYAPI Key header not provided.
401INVALID_API_KEYAPI Key invalid, revoked, or expired.
404EXECUTION_NOT_FOUNDThe execution_id does not exist or does not belong to the integrator.
GET /api/v1/onboarding/executions

Returns a paginated list of executions. By default, executions that are terminal (approved, rejected, blocked, expired, cancelled) or still in the initial created state (created but never advanced past the first step submission) are excluded from the response; pass an explicit status to include them (for example, status=created returns the excluded shell rows).

Results are ordered by created_at descending, with execution_id descending as a deterministic tie-breaker between rows created at the same instant.

ParameterTypeDefaultDescription
pageinteger1Requested page.
per_pageinteger20Items per page (maximum: 100). Values above 100 are clamped.
reference_idstring (≤ 255)(unset)Filter by exact, case-sensitive match on the execution’s reference_id. Useful for retrieving every execution tagged with a given correlator, such as the integrator’s own customer ID, account ID, or order number.
flow_idUUID(unset)Filter by the flow that originated the executions. A malformed UUID returns 400 INVALID_REQUEST.
statusstring(unset)Filter by execution status code (e.g., created, in_progress, pending_review, approved, rejected, blocked, expired, cancelled). When provided, the default exclusion set (terminal states plus created) is bypassed, so status=approved returns approved executions and status=created returns shell rows that would otherwise be hidden. Unknown codes return an empty page.
namestring(unset)Filter by case-insensitive substring on the customer’s name. Matches both customers_person.name (kyc, kyc_minor, kyc_representative) and customers_company.company_name (kyb).
tax_idstring(unset)Filter by exact match on the customer’s tax identifier. Accepts CPF (11 digits) or CNPJ (14 digits), with or without punctuation: 383.417.298-73, 38.341.729/0001-87, 38341729873, and 38341729000187 are all normalized to digits before comparison. Anything else returns 400 INVALID_REQUEST.

Filters are combinable: ?reference_id=SOL-2026-00042&status=approved returns approved executions whose reference_id matches exactly; ?name=silva&status=in_progress returns in-progress executions whose customer name contains silva.

{
"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": 9,
"steps_total": 19,
"customer": {
"full_name": "Marcus Vinicius Sales",
"tax_id": "23117555854",
"tax_id_masked": "***175.558**",
"email": "msales@exemplo.com.br",
"is_pep": false,
"has_foreign_tax_obligation": false
},
"links": {
"url": "https://acme.onp.prod.guardline.com.br/onboarding/link/xyz987def456",
"embed_url": "https://acme.onp.prod.guardline.com.br/embed/t/xyz987def456"
},
"data_divergences": [],
"created_at": "2026-03-31T09:00:00Z",
"started_at": "2026-03-31T09:03:00Z",
"expires_at": "2026-04-07T09:00:00Z"
}
],
"pagination": {
"page": 1,
"per_page": 20,
"total": 1,
"total_pages": 1
}
}
}

The customer block carries the same shape on the list and on the per-id detail endpoint. On the list, only summary identification fields are populated (full_name, tax_id, tax_id_masked, email, is_pep, has_foreign_tax_obligation). Address, document, and biometric sub-blocks plus the decision envelope are only returned by GET /api/v1/onboarding/executions/{execution_id}. The customer field is omitted (rather than emitted as null) on rows where no customer record is linked yet (e.g., probes or sessions abandoned before the first step). For kyb executions, tax_id is the CNPJ (14 digits) and email is omitted (companies do not carry an email on the customer record).

The tax_id field is the raw identifier (CPF or CNPJ, digits only). The legacy tax_id_masked is still emitted for backwards compatibility and will be removed in a future version; new integrations should consume tax_id.

The links block, when present, carries the tenant portal URLs the customer or your embed can use to resume or continue the journey: url is the direct link users open in a browser, embed_url is the iframe-friendly variant. Both are constructed from the execution’s link token and the tenant’s onboarding hostname (the same token POST /onboarding/sessions returns via its top-level url field). The block is emitted on both the list and the per-id detail endpoint, and is omitted (not emitted as null) for orphan executions that have no linked token, so integrators can safely condition rendering on the field’s presence.

HTTPCodeCause
400INVALID_REQUESTflow_id is not a valid UUID, reference_id exceeds 255 characters, or tax_id is not 11 or 14 digits after normalization.
401MISSING_API_KEY / INVALID_API_KEYAuthentication missing or invalid.
POST /api/v1/executions/{execution_id}/cancel

Transitions 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.

Accepts either:

  • X-API-Key (gl_live_* or gl_test_*), or
  • Authorization: Bearer <JWT> (backoffice JWT).

Honors the Idempotency-Key header; see Idempotency.

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"
}
FieldTypeRequiredDescription
reasonstring (max 500)NoFree-text reason recorded in audit and event payload.
{
"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"
}
}
FieldTypeDescription
execution_idUUIDExecution that was cancelled.
cancelled_childrenUUID arrayUUIDs of child executions cancelled in cascade (the representative under a kyc_minor). Empty when the execution has no children.
status_codestringAlways cancelled on success.
cancelled_atISO 8601Cancellation timestamp.
HTTPCodeCause
400INVALID_REQUESTThe path UUID is malformed.
401MISSING_TOKEN / INVALID_API_KEYAuthentication missing, invalid, or expired.
404NOT_FOUNDNo execution with the given UUID.
422UNPROCESSABLEExecution already in a terminal state (approved, rejected, cancelled, expired, blocked).
429RATE_LIMITEDRate limit exceeded.

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.

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. Today this covers the legal representative for kyc_minor; future flow types (such as kyb officers) will reuse the same family. Emitted on the parent execution, with the linked subject’s outcome reflected in the payload.
EventDescription
onboarding.startedSession created via POST /sessions.
onboarding.completedAll steps submitted; decision engine processing.
onboarding.approvedApproved by the engine or by an analyst.
onboarding.rejectedRejected by the engine or by an analyst.
onboarding.reviewReferred for manual review.
onboarding.blockedJourney interrupted by a security rule.
onboarding.expiredSession expired before completion.
onboarding.cancelledCancelled by the integrator or by an administrator.
EventDescription
linkage.pendingThe 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.startedThe legal representative accessed their link and submitted the first step.
linkage.completedThe legal representative completed their journey; the parent’s consolidation has been triggered.
linkage.rejectedAn analyst rejected the representative’s execution in manual review.
linkage.expiredThe representative’s execution expired before completion.

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.

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.

Automatic approval:

onboarding.started (1) session created
onboarding.completed (2) all steps submitted
onboarding.approved (3) approved by engine

Rejection:

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)

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 created
linkage.pending (2) minor completed; parent in pending_representative
linkage.started (3) representative accessed their link
linkage.completed (4) representative finished
onboarding.completed (5) consolidation; decision engine processing
onboarding.approved (6) approved by engine

Rejection:

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 expired
onboarding.expired (4) parent expired as a consequence

Representative 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 representative
onboarding.rejected (8) parent decision propagates
{
"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",
"data": {
"decision": {
"result": "approved",
"risk_level": "low",
"decided_by": "engine",
"decided_at": "2026-04-01T10:45:00Z"
},
"liveness": {
"status": "executed",
"result": "passed"
}
}
}
FieldDescription
eventEvent type.
execution_idParent 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_typeJourney type.
reference_idIntegrator identifier, as sent during session creation.
event_sequenceSequential event number within the execution, for canonical ordering.
timestampEvent emission timestamp in ISO 8601.
dataOptional object carrying the decision and proof-of-life blocks below. Absent when the event has neither.

Present on events that carry a decision or a linkage state transition (onboarding.approved/rejected/review/blocked and linkage.*).

FieldDescription
resultapproved, rejected, pending_review, blocked, or the linkage state (pending_representative, in_progress, completed, expired).
risk_levelDerived from the decision score: low (score up to 150), medium (up to 350), high (up to 700), critical (above 700).
decided_byAlways engine on webhook payloads. The query endpoint may report manual for analyst decisions.
decided_atDecision timestamp in ISO 8601.

Present on terminal events (onboarding.completed/approved/rejected/review/blocked/expired). The proof-of-life step (iProov) is modular per flow: a journey may run it, skip it by configuration, or belong to a flow variant without it, and the integrator can toggle it per session via the biometric field on POST /api/v1/onboarding/sessions. This block lets the integrator tell “not executed by configuration” apart from “failed”.

statusMeaning
executedThe proof of life ran and passed. Step completion requires passed liveness evidence, so executed implies approval; result: "passed" accompanies it.
not_executedThe flow carries the step but the journey ended without completing it (failure, attempt cap, abandonment).
skipped_by_configThe step exists in the flow but was switched off by configuration: disabled on the flow, disabled for this session ("biometric": false), or an unmet flow condition.
not_configuredThe flow variant has no proof-of-life step.

The result field is present only when status is executed and currently only takes passed. Best-effort semantics: if the block cannot be resolved at emission time it is omitted; its absence must not fail the integrator’s parser.

Notes:

  • **Absence of the block means “unknown”, never not_configured.** Envelopes emitted before this contract existed also lack it. Do not infer configuration from the absence of the liveness object; “the journey has no proof of life” is signaled exclusively by the explicit not_configured value.
  • onboarding.cancelled deliberately does NOT carry the block: it is scoped to outcome-bearing terminal events, and a cancellation is not an outcome.
HeaderDescription
Content-TypeAlways application/json.
X-Guardline-Event-IDStable event UUID, identical across retries. Use for deduplication.
X-Guardline-Delivery-IDUnique UUID per delivery attempt. Changes on each retry.
X-Guardline-Attempt-NumberSequential attempt number (1 for first delivery, 2 for first retry, etc.).
X-Guardline-TimestampUnix timestamp of the signature moment (when HMAC is enabled).
X-Guardline-SignatureHMAC-SHA256 signature in hexadecimal (when HMAC is enabled).
AuthorizationBearer token (when OAuth is enabled).

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.

ModeHMAC SignatureOAuth BearerClient Certificate
HMAC only (default)YesNoNo
OAuth 2.0YesYesNo
mTLSYesNoYes
OAuth 2.0 + mTLSYesYesYes

HMAC authentication uses a shared secret provisioned during integrator setup in the admin panel.

Verification process on the receiver:

  1. Extract the X-Guardline-Timestamp header.
  2. Reject if the timestamp is older than 5 minutes relative to the server clock.
  3. Concatenate timestamp and request body: {timestamp}.{body}.
  4. Compute HMAC-SHA256 of the concatenation using the shared secret.
  5. Encode the result in hexadecimal.
  6. Compare with the X-Guardline-Signature header using constant-time comparison.
  7. 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)

Mutual TLS authentication ensures Guardline’s identity at the transport layer, without requiring application-level validation.

Setup:

  1. During provisioning, Guardline provides the root CA certificate (guardline-ca.pem).
  2. The integrator configures their webhook server to require client certificates.
  3. The integrator adds guardline-ca.pem to the server’s trusted CA list.
  4. On each delivery, Guardline presents a client certificate signed by the root CA.
  5. 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 authentication allows Guardline to obtain an access token before each delivery, using the Client Credentials flow (RFC 6749, section 4.4).

Setup:

  1. The integrator provides in the admin panel: token_endpoint, client_id, client_secret, and optionally scope.
  2. Before each delivery, Guardline requests a token from the provided endpoint.
  3. 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}
  1. Guardline includes the obtained token in the webhook authorization header:
Authorization: Bearer {access_token}
  1. 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.

The webhook endpoint is configured in the Guardline admin panel. Requirements:

RequirementDetail
ProtocolHTTPS required.
TLS version1.2 or higher.
Success responseAny 2xx code (body ignored).
TimeoutRespond within 10 seconds.
IdempotencyDeduplicate 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.

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.

When the integrator’s endpoint returns a non-2xx code or does not respond within 10 seconds, Guardline resends the notification with exponential backoff:

AttemptInterval after failure
15 seconds
230 seconds
32 minutes
410 minutes
530 minutes
61 hour
72 hours
84 hours
98 hours
1012 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.

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.

GET /api/v1/onboarding/notifications
ParameterTypeDefaultDescription
statusstringpendingFilter by state: pending, failed, retrying. Comma-separated for multiple values.
pageinteger1Requested page.
per_pageinteger50Items per page (maximum: 200).

Response (200):

{
"error": false,
"data": {
"items": [
{
"delivery_id": "d4e5f6a7-b8c9-0123-def0-123456789abc",
"execution_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"event": "onboarding.approved",
"event_sequence": 6,
"status": "pending",
"attempts": 3,
"max_attempts": 10,
"last_attempt_at": "2026-04-01T10:48:00Z",
"created_at": "2026-04-01T10:45:00Z"
}
],
"total": 1,
"page": 1,
"per_page": 50,
"total_pages": 1
}
}
POST /api/v1/onboarding/notifications/{delivery_id}/ack

Marks 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"
}
}
CodeMeaningGuidance
200SuccessProcess normally.
201Resource createdProcess normally.
400Invalid requestFix the payload. Do not retry.
401Authentication failedCheck the API Key (or JWT for resend-representative-link). Do not retry.
404Resource not foundCheck the identifiers. Do not retry.
409ConflictHandle according to the specific error code.
410GoneThe resource was valid but is no longer accessible (e.g., expired link).
429Rate limitedWait for the interval indicated in the Retry-After header.
500Internal errorRetry with exponential backoff (maximum 3 attempts).
502Internal error during processingRetry with exponential backoff.

Note: error message fields are localized in pt-BR. Match on code for programmatic branching.

CodeHTTPDescription
MISSING_API_KEY401API Key header not provided.
INVALID_API_KEY401API Key invalid, revoked, or expired.
MISSING_TOKEN401JWT not provided (resend-representative-link only).
INSUFFICIENT_PERMISSIONS403JWT lacks the required role.
INVALID_REQUEST400Malformed JSON, unknown field, or wrong field type.
VALIDATION_FAILED400Field-level validation failed.
LINKED_SUBJECTS_REQUIRED400kyc_minor request with missing/empty/oversized linked_subjects or wrong role.
INVALID_FLOW_TYPE400Flow type not active for the integrator, or flow_id resolves to an internal-use flow type.
MISSING_REQUIRED_FIELDS400kyc_minor missing primary.tax_id or primary.full_name.
ACTIVE_SESSION_EXISTS409Active session exists for the same primary subject and flow.
EXECUTION_NOT_FOUND404Execution not found or does not belong to the integrator.
EXECUTION_TERMINATED409The execution has already reached a terminal state.
NO_RESENDABLE_CHILD404No representative child to resend (resend-representative-link only).
LINK_NOT_FOUND404Link token does not exist.
LINK_EXPIRED410Link token expired.
FLOW_NOT_FOUND404Flow not found.
NOTIFICATION_NOT_FOUND404Notification not found.
RATE_LIMITED429Request limit exceeded.
ONBOARDING_ERROR502Internal error during session creation.
MethodPathAuthDescription
GET/api/v1/onboarding/flowsAPI KeyList available journeys.
POST/api/v1/onboarding/sessionsAPI KeyCreate an onboarding session (with optional linked subjects).
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 KeyQuery execution with complete data.
GET/api/v1/onboarding/executionsAPI KeyList active executions.
POST/api/v1/onboarding/executions/{execution_id}/resend-representative-linkAdmin/Supervisor JWTResend the representative link via SMS/WhatsApp.
POST/api/v1/executions/{execution_id}/cancelAPI KeyCancel an execution (and its representative child for kyc_minor), releasing the active-session slot for reuse.
GET/api/v1/onboarding/notificationsAPI KeyPoll pending webhook notifications.
POST/api/v1/onboarding/notifications/{delivery_id}/ackAPI KeyAcknowledge a webhook notification.