Pular para o conteúdo

ONP Embed Integration

This document describes how to embed the Guardline onboarding experience directly within your application using an iframe. The embedded mode allows your users to complete identity verification without leaving your interface, while Guardline handles the entire onboarding flow, from data collection to document capture, biometric verification, and automated decision-making.

The integration model follows a clear separation of responsibilities: Guardline verifies and decides, the integrator orchestrates the experience. Guardline does not read from or write to the integrator’s internal systems. All results (approval, rejection, review referral) are communicated outbound via real-time PostMessage events to the frontend and via signed webhooks to an endpoint provided by the integrator.

The embedded mode and the direct link mode share the same infrastructure: same API, same backend, same decision engine, same verification providers. The only difference is the entry point and the presence of the PostMessage channel for real-time communication with the host page.

Four principles guide the embedded integration:

  • Transparency for the end user: The onboarding must feel like a native part of the integrator’s application. The user should not perceive that they are interacting with an external system. Visual identity (colors, logos, text overrides) is fully configurable per flow and is managed by the Guardline backend, not by URL parameters or client-side code.
  • Security by default: The iframe operates in a cross-origin context, meaning the integrator’s site and the Guardline onboarding run on different domains. All communication between the two uses secure channels: PostMessage with strict origin validation, webhooks with HMAC-SHA256 signatures. Session tokens are ephemeral, cryptographically secure, and bound to a single execution.
  • Backend as the source of truth: All persistent information (session state, collected data, verification results, visual configuration) lives in the Guardline backend. The iframe frontend does not maintain state that cannot be reconstructed from the backend. This ensures sessions can be resumed even if the iframe is destroyed and recreated.
  • Same infrastructure, two entry points: The embedded mode and the direct link mode share the entire stack. The frontend automatically detects whether it is running inside an iframe and activates the PostMessage channel accordingly. No separate configuration is needed.

The embedded onboarding involves four participants communicating in sequence: the integrator’s backend, the Guardline API, the integrator’s frontend, and the Guardline iframe.

The process starts when the integrator’s backend decides a user needs to be verified. It calls the Guardline API with its API key, specifying the flow type and any known user data. The API creates an execution, generates a session token, and returns both to the integrator.

The integrator’s frontend uses the token to mount the iframe. The iframe loads, resolves the token with the Guardline backend, and receives everything it needs to render the onboarding: flow configuration, visual identity, and pre-fill data.

The user navigates through the onboarding steps inside the iframe. Each completed step is persisted to the backend. Simultaneously, the iframe sends PostMessage events to the host page reporting progress.

When the last step is completed, the backend automatically runs the decision engine. The iframe communicates the conclusion to the host page via PostMessage, and the backend fires a webhook to the integrator’s backend with the result.

The starting point of every integration is creating an onboarding session from the integrator’s backend.

POST /api/v1/onboarding/sessions

All API requests must include a valid API key in the X-API-Key header.

X-API-Key: gl_live_a1b2c3d4e5f6g7h8i9j0

API keys are provisioned by the Guardline team and are scoped per environment (gl_live_ for production, gl_test_ for sandbox). Keep your API key confidential. If compromised, contact Guardline immediately for rotation.

All endpoints require HTTPS. HTTP requests will be rejected.

Each integrator receives a dedicated instance with its own base URL. All API paths in this document are relative to that base URL.

EnvironmentBase URL PatternAPI Key Prefix
Sandboxhttps://{instance}.onp.dev.guardline.com.brgl_test_
Productionhttps://{instance}.onp.prod.guardline.com.brgl_live_

The exact base URL for your instance is provided by the Guardline team during onboarding. Optionally, the integrator can use a custom domain (e.g., https://verify.integrator.com) via CNAME.

Content-Type: application/json

FieldTypeRequiredDescription
flow_typestringyesOnboarding flow type: kyc (individuals), kyb (businesses), or kyc_minor (minors aged 14-17 with legal representative). Determines which verification steps are included.
flow_idstringnoUUID of a specific flow to use. When provided, takes precedence over flow_type for flow selection. Useful when multiple flows of the same type are configured.
reference_idstringnoYour internal identifier for this user or process. Returned in webhooks and queryable via API.
redirect_urlstringnoURL to redirect the user after completion in direct link mode. Ignored in iframe mode.
pre_fillobjectnoKnown user data for pre-filling form fields (see below). For kyc_minor, birth_date is required and must correspond to age 14-17.

When the integrator already knows user data, it can send it in the pre_fill object. The onboarding frontend will populate the corresponding fields automatically. The user sees the data already filled in and advances faster.

Fields that require the user’s physical presence (document capture, biometric verification, terms acceptance) are never skipped, regardless of which data is pre-filled.

FieldTypeDescription
full_namestringUser’s full name
tax_idstringCPF (individuals) or CNPJ (businesses)
emailstringEmail address
phonestringPhone number with country code
birth_datestringDate of birth (ISO 8601: YYYY-MM-DD)

For kyc_minor flows, the system validates the birth date at session creation. The age must be between 14 and 17 years (inclusive). Sessions for minors under 14 are rejected, and users aged 18 or older should use the standard kyc flow instead.

{
"flow_type": "kyc",
"reference_id": "USR-2026-00042",
"pre_fill": {
"full_name": "Maria Santos",
"tax_id": "123.456.789-00",
"email": "maria@example.com"
}
}
{
"error": false,
"data": {
"execution_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"token": "f8c2e1a4b7d9036e5c8f1a2b3d4e5f6a",
"url": "https://acme.onp.prod.guardline.com.br/onboarding/link/f8c2e1a4b7d9036e5c8f1a2b3d4e5f6a",
"embed_url": "https://acme.onp.prod.guardline.com.br/embed/f8c2e1a4b7d9036e5c8f1a2b3d4e5f6a",
"expires_at": "2026-04-02T00:00:00Z"
}
}
FieldTypeDescription
execution_idUUIDUnique identifier for this onboarding execution. Use it to query status via API.
tokenstringSession token (32-character hex). Used to mount the iframe or generate the direct link. Stored as a hash in the database, never in plain text.
urlstringFull URL for direct link mode (user opens in browser).
embed_urlstringFull URL for iframe mode (use as the iframe src).
expires_atdatetimeISO 8601 expiration timestamp for the link validity window (default: 7 days). Once the user starts the onboarding, a separate 24-hour active session window applies (see Token Expiration).

The token is a 32-character hexadecimal value generated from a cryptographically secure source. It is stored as a hash in the database and bound to a single execution. The token can be used multiple times to access the same execution (supporting session resumption), but it can never initiate a different execution.

The token is permanently invalidated when the execution reaches a terminal state (approved, rejected, blocked, or expired). The completed state is not terminal: it is a transient state where the decision engine is processing, and the session may still transition to approved, rejected, or in_review. From a terminal state, any attempt to load the iframe with that token displays an informative screen appropriate to the scenario.

Two time windows govern the token lifecycle, and the stricter one always takes precedence:

WindowDurationStarts WhenPurpose
Link validity7 days (default, configurable)Session is created via APIGives the user time to open the link. Covers scenarios where the user receives a notification and opens it days later.
Active session24 hoursUser opens the iframe for the first timeLimits the exposure of an active session. Once started, the user has 24 hours to complete all steps.

If the user never opens the iframe, the token expires after the link validity window (7 days). If the user opens the iframe on day 6, the active session window (24 hours) starts, and the token expires on day 7 (whichever comes first). If the user opens the iframe on day 1, they have 24 hours to complete, even though the link would otherwise be valid for 6 more days.

The session token is present in the iframe URL path. This is a deliberate trade-off: it allows stateless embedding without requiring the host page to inject credentials via PostMessage before the iframe can initialize. The following mitigations limit the exposure surface:

Leakage VectorMitigation
Referer headerThe Guardline backend sets Referrer-Policy: strict-origin-when-cross-origin on all responses. Outbound requests from the iframe (to verification providers, CDNs) send only the origin (https://acme.onp.prod.guardline.com.br), never the full path containing the token.
Browser historyThe token URL is loaded inside an iframe, not in the top-level navigation bar. Iframe navigations are not recorded in the browser’s address bar or history.
Server-side logsThe Guardline backend does not log full request paths containing tokens. Access logs record the route pattern (/embed/:token) without the token value.
Analytics and third-party scriptsThe onboarding frontend does not include generic third-party analytics scripts. Provider SDKs are loaded only for verification purposes, and Guardline does not forward token values as analytics identifiers.
Integrator logsThe integrator should treat the embed_url and token fields from the session creation response as sensitive data. Avoid logging these values in plain text. The execution_id is the safe identifier for logging and correlation.

If the same reference_id is submitted while a valid session already exists for that user, the API returns the existing session instead of creating a new one.

The integrator’s frontend embeds the onboarding by mounting an iframe element pointing to the embed_url returned by the session creation endpoint.

The iframe requires explicit permission declarations to function correctly. Without these, the browser silently blocks access to the camera and sensors, and the onboarding cannot proceed past biometric verification.

AttributeValuePurpose
srcembed_url from session creation responsePoints to the Guardline onboarding for this session.
allowcamera; accelerometer; gyroscope; magnetometerCamera is required for document capture and biometric verification. Motion sensors (accelerometer, gyroscope, magnetometer) are required on mobile devices for liveness detection.
width100%The onboarding layout is responsive and adapts to the container width.
height100% of viewport or fixed value (minimum 600px)The layout is designed to occupy the full height of its container.

The sandbox attribute must not be used. It restricts iframe capabilities (forms, scripts, popups, camera access) in ways that make the onboarding inoperable.

The iframe height should be set by the integrator with a fixed value, typically 100% of the viewport. The onboarding layout is designed to fill the entire container.

Automatic height resizing based on content is available via the height-changed PostMessage event. The iframe reports its content height whenever it changes, and the host page can adjust the iframe element accordingly. This approach is optional and works best for layouts where the iframe is not full-viewport.

The visual identity of the onboarding is entirely dynamic and managed by the Guardline backend. No visual parameters are passed via URL, query strings, or client-side code.

When the iframe resolves the session token, the backend returns the complete visual configuration associated with the flow:

SettingDescription
Primary and secondary colorsIn dark and light variants, used for buttons, accents, and interactive elements.
Logo URLsHosted on CDN, in versions for dark and light backgrounds.
Theme modeDark, light, or automatic (follows the user’s system preference).
Button stylingBackground, text, and icon colors per theme.
Per-step overridesIndividual steps can override base colors and layout settings.

The frontend applies these settings as CSS custom properties at render time. Changing the visual identity does not require code changes or redeployment: update the settings in the Guardline admin panel or via API, and the next session will reflect the new configuration.

This design ensures visual consistency between the direct link mode and the embedded mode. Both use the same configuration source (the backend), so the integrator does not need to synchronize visual parameters across multiple integration points.

The iframe and the host page communicate exclusively via PostMessage, the browser’s native mechanism for cross-origin window communication. Every message sent by the iframe carries a fixed identifier (guardline-onboarding) that allows the host page to filter relevant messages.

All messages follow this format:

{
"source": "guardline-onboarding",
"event": "event-name",
"payload": {}
}

The source field is always guardline-onboarding. Use it to filter messages from the Guardline iframe and ignore messages from other sources.

Emitted once, after the iframe loads, resolves the session token, and is ready to display the first step.

Payload FieldTypeDescription
executionIdUUIDThe execution identifier for this onboarding session. Use it to correlate the embedded session with your internal records.

The host page can use this event to hide a loading indicator and reveal the iframe.

Emitted each time the user advances to the next step or navigates back.

Payload FieldTypeDescription
executionIdUUIDThe execution identifier.
stepIdstringThe identifier of the current step (e.g., personal-data, document-front, biometric).
stepIndexintegerZero-based index of the current step in the flow.
totalStepsintegerTotal number of steps in the flow.

The host page can use this event to display an external progress indicator, track analytics, or adjust the surrounding interface.

Emitted when the user has submitted all required steps. This event signals that the user’s part of the journey is done, not that a final decision has been made. The decision field provides an immediate indication of the outcome, but the webhook is the authoritative source for the final decision result.

Payload FieldTypeDescription
executionIdUUIDThe execution identifier.
decisionstringImmediate decision indication: approved, rejected, or in_review. When in_review, the execution has been referred to a human analyst and the final result will arrive exclusively via webhook (onboarding.approved or onboarding.rejected).

Typical frontend handling by decision value:

decisionSuggested UX
approvedClose the iframe, display a success confirmation.
rejectedClose the iframe, display an appropriate message or redirect to a support flow.
in_reviewClose the iframe, inform the user that verification is under review and they will be notified. The integrator’s backend will receive the final result via webhook.

Emitted when the flow is interrupted for security reasons.

Payload FieldTypeDescription
executionIdUUIDThe execution identifier.
reasonstringThe reason for the block: fraud_detected, invalid_data, max_attempts_exceeded, identity_mismatch.

The host page can display a support message or offer alternative channels.

Emitted when a technical error prevents progress.

Payload FieldTypeDescription
executionIdUUIDThe execution identifier.
codestringA standardized error code (see Error Reference).
messagestringA human-readable description of the error.

Emitted whenever the iframe’s content height changes. This is useful for layouts where the iframe is not set to 100% of the viewport.

Payload FieldTypeDescription
heightintegerThe content height in pixels.

PostMessage is an open channel by nature: any window can send messages to any other. Security depends on rigorous validation on both sides.

On the iframe side, messages are sent exclusively to the origin configured for the integrator’s instance. The Guardline backend maintains the list of allowed origins, and the iframe restricts PostMessage delivery to those domains. Messages sent with a restricted origin are rejected by the browser if the recipient is on a different domain.

On the integrator side, the host page must verify the origin property of every received message against the exact domain of the Guardline instance. Validation by substring or regex is insecure (a domain like guardline.com.br.attacker.com would pass naive validations). Only exact domain equality should be used.

In the current version, the host page does not need to send commands to the iframe. The onboarding is autonomous once started: the user navigates through the steps, and the iframe manages all state internally.

Future versions will support commands such as programmatically restarting the flow or terminating the session.

An onboarding session transitions through a defined set of states from creation to final resolution.

StateTerminalDescription
createdNoSession exists but the user has not started the onboarding yet.
in_progressNoUser has opened the iframe and is navigating through steps.
completedNoAll steps have been submitted. The decision engine is processing. This is a transient state.
approvedYesThe user has been approved (automatically or by an analyst).
rejectedYesThe user has been rejected (automatically or by an analyst).
in_reviewNoThe execution has been referred to a human analyst for manual review.
blockedYesThe flow was interrupted during the onboarding due to a security rule (fraud detection, excessive biometric attempts, identity mismatch).
expiredYesThe session expired before completion. The token is invalidated.

Retrieve the current state of an onboarding execution at any time.

GET /api/v1/onboarding/executions/{execution_id}

Headers:

X-API-Key: gl_live_a1b2c3d4e5f6g7h8i9j0

Response:

{
"error": false,
"data": {
"execution_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "approved",
"flow_type": "kyc",
"reference_id": "USR-2026-00042",
"steps_completed": 6,
"steps_total": 6,
"decision": {
"result": "approved",
"risk_level": "low",
"decided_at": "2026-03-26T15:30:00Z",
"decided_by": "engine"
},
"created_at": "2026-03-26T14:00:00Z",
"started_at": "2026-03-26T14:05:00Z",
"completed_at": "2026-03-26T14:12:00Z"
}
}

For kyc_minor flows, the response includes a representative object with the legal representative’s verification status:

{
"representative": {
"full_name": "Maria Santos",
"tax_id_masked": "987.***.**1-00",
"relationship": "mother",
"guardianship_document": null,
"status": "completed"
}
}

The relationship field indicates the bond type: mother, father, or other (tutor, curator, grandparent). When the relationship is other, guardianship_document contains "uploaded" if the representative submitted proof of legal guardianship.

The onboarding can be interrupted for many reasons: the user closes the browser, loses connectivity, navigates to another page, or the host page destroys and recreates the iframe. In all these cases, the user can resume from where they left off by accessing the same token.

Resumption works because the backend persists the state of each step individually. When the iframe loads with a token, it queries the backend for the execution state. If the execution is in progress, the frontend identifies the first pending step and positions the user there. Data already submitted in previous steps does not need to be entered again.

This approach is critical in the iframe context. The browser’s sessionStorage is isolated by origin in cross-origin iframes. If the host page destroys the iframe and recreates it (for example, because the user navigated away and came back), all sessionStorage is lost. The backend as the source of truth ensures the session survives any iframe destruction scenario.

If the token is expired, the execution is in a terminal state (approved, rejected, blocked, or expired), or the token is invalid, the iframe displays an informative screen appropriate to the scenario and does not attempt to start a new flow.

PostMessage communicates events to the integrator’s frontend in real time, but it is a volatile channel: if the host page is not listening, the message is lost. Webhooks ensure that the integrator’s backend is notified reliably and persistently.

EventDescription
onboarding.startedThe user accessed the link and initiated the onboarding process.
onboarding.completedAll required steps were submitted and decision processing started.
onboarding.approvedThe user was approved (automatic or manual decision).
onboarding.rejectedThe user was rejected (automatic or manual decision).
onboarding.reviewThe execution was referred to a human analyst for review.
onboarding.blockedThe flow was interrupted due to a security rule (fraud detection, excessive biometric attempts, identity mismatch). Terminal state.
onboarding.expiredThe session expired without completion.

For kyc_minor flows, three additional events track the legal representative’s progress:

EventDescription
representative.pendingThe minor completed their part. The system is waiting for the legal representative to start.
representative.startedThe legal representative opened the link and began the verification.
representative.completedThe legal representative finished all required steps. The final decision follows.

Events for a given execution follow these emission-order rules:

  1. onboarding.started is emitted when the user opens the onboarding flow.
  2. If all required steps are submitted, onboarding.completed is emitted.
  3. After onboarding.completed, exactly one of onboarding.approved, onboarding.rejected, or onboarding.review is emitted.
  4. If onboarding.review is emitted, exactly one of onboarding.approved or onboarding.rejected follows when the analyst resolves the case.
  5. onboarding.blocked may be emitted only while the execution is in_progress, and it is terminal. No other events follow it.
  6. onboarding.expired is emitted if the session expires before a terminal decision, and it is terminal. No other events follow it. For sessions never started, onboarding.expired may be the only event.
  7. For kyc_minor flows, after onboarding.completed, representative.pending is emitted. When the representative opens the link, representative.started follows. When the representative finishes, representative.completed is emitted, followed by the final decision event (onboarding.approved, onboarding.rejected, or onboarding.review).

Due to retries and network conditions, the integrator may receive events out of delivery order. Each webhook payload includes an event_sequence field (monotonically increasing integer per execution) that represents the canonical emission order. The integrator should use event_sequence to determine the correct order, not timestamp or delivery order.

The maximum delay between event emission and first delivery attempt is 5 seconds under normal conditions. If the integrator’s endpoint is unreachable, the retry policy applies (see Retry Policy).

All webhook payloads follow a consistent structure:

{
"event": "onboarding.approved",
"execution_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"reference_id": "USR-2026-00042",
"flow_type": "kyc",
"event_sequence": 3,
"timestamp": "2026-03-26T15:30:00Z",
"data": {}
}
FieldTypeAlways PresentDescription
eventstringyesThe event type (see table above).
execution_idUUIDyesThe execution identifier.
reference_idstringyesThe integrator’s reference ID, as provided during session creation.
flow_typestringyesThe flow type (kyc, kyb, or kyc_minor).
event_sequenceintegeryesMonotonically increasing integer per execution. Use this to determine canonical event order (see Event Ordering).
timestampdatetimeyesISO 8601 timestamp of the event emission.
dataobjectyesEvent-specific payload (see below).
{
"event": "onboarding.approved",
"execution_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"reference_id": "USR-2026-00042",
"flow_type": "kyc",
"event_sequence": 3,
"timestamp": "2026-03-26T15:30:00Z",
"data": {
"decision": {
"result": "approved",
"risk_level": "low",
"decided_by": "engine",
"decided_at": "2026-03-26T15:30:00Z"
},
"customer": {
"full_name": "Maria Santos",
"tax_id_masked": "123.***.**9-00"
},
"steps_completed": 6,
"steps_total": 6,
"started_at": "2026-03-26T14:05:00Z",
"completed_at": "2026-03-26T14:12:00Z",
"duration_seconds": 420
}
}

All decision events (onboarding.approved, onboarding.rejected, onboarding.review, onboarding.blocked) share the same payload structure. The fields that vary per event:

Eventdata.decision.resultdata.decision.reason_codedata.decision.reason_description
onboarding.approvedapprovednull (automatic) or analyst reasonnull or analyst description
onboarding.rejectedrejectede.g., DOCUMENT_FRAUD_DETECTEDe.g., “Document tampering detected during verification”
onboarding.reviewin_reviewe.g., BIOMETRIC_LOW_CONFIDENCEe.g., “Biometric verification returned low confidence score”
onboarding.blockedblockede.g., MAX_ATTEMPTS_EXCEEDEDe.g., “Excessive biometric verification attempts”

The onboarding.expired event has a different data structure since no decision was made:

{
"event": "onboarding.expired",
"execution_id": "d4e5f6a7-b890-1234-efab-567890123456",
"reference_id": "USR-2026-00045",
"flow_type": "kyb",
"event_sequence": 1,
"timestamp": "2026-03-27T14:00:00Z",
"data": {
"steps_completed": 2,
"steps_total": 8,
"last_step": "company-data",
"created_at": "2026-03-26T14:00:00Z",
"expired_reason": "session_timeout"
}
}

Every webhook request includes the following HTTP headers:

HeaderDescription
Content-TypeAlways application/json
X-Guardline-Event-IDUnique UUID for this event. Stable across retries of the same event. Use this for deduplication.
X-Guardline-Delivery-IDUnique UUID for this specific delivery attempt. Changes on each retry. Use this for delivery tracking and debugging.
X-Guardline-SignatureHMAC-SHA256 hex-encoded signature of the request body
X-Guardline-TimestampUnix timestamp of when the signature was computed

The X-Guardline-Signature header contains a hex-encoded HMAC-SHA256 hash computed over the concatenation of the timestamp and the raw request body. This prevents an attacker from reusing a legitimate payload captured previously (replay attack protection).

To verify:

  1. Read the X-Guardline-Timestamp header.
  2. Verify that the timestamp is within an acceptable window (recommended: 5 minutes).
  3. Concatenate the timestamp and the raw request body as bytes: timestamp + "." + body.
  4. Compute HMAC-SHA256(concatenated_value, shared_secret) and hex-encode the result.
  5. Compare your computed signature with the X-Guardline-Signature header value using a constant-time comparison.
  6. If they do not match, reject the webhook.

Pseudocode:

import hmac, hashlib, time
def verify_webhook(body: bytes, timestamp: str, signature: str, secret: str) -> bool:
# Reject if timestamp is older than 5 minutes
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)

Webhook secrets use the whsec_ prefix for identification and are encrypted at rest using AES-256-GCM. The raw secret is displayed once at creation time and cannot be retrieved afterward. If the secret is lost, a new one must be created via the API or admin panel, which deactivates the previous version. Secret rotation is supported through versioning: create a new secret before revoking the old one to avoid delivery interruptions.

If Guardline does not receive an HTTP 2xx response, the webhook is retried with exponential backoff:

AttemptDelay 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. Failed deliveries can be resent manually from the Guardline admin panel, which also displays the full history of attempts, HTTP status codes, and response times for each delivery.

Timeout: Each delivery attempt has a 10-second timeout. Ensure your webhook handler responds within this window.

Expected response: Return any HTTP 2xx status code. The body is ignored.

Webhooks may be delivered more than once (due to retries after network issues). Use the X-Guardline-Event-ID header to deduplicate:

  1. Store each processed X-Guardline-Event-ID.
  2. On receiving a webhook, check if the event ID was already processed.
  3. If yes, respond 2xx without reprocessing.

The X-Guardline-Event-ID is stable across retries of the same event. The X-Guardline-Delivery-ID is unique per attempt and should be used only for delivery tracking and debugging, not for deduplication.

The integrator’s webhook endpoint must meet the following requirements:

RequirementDescription
HTTPSMandatory. Protects the payload and signature in transit.
2xx responseReturn any 2xx status to confirm receipt.
10-second timeoutRespond within 10 seconds. Process asynchronously if needed.
IdempotentHandle duplicate deliveries using X-Guardline-Event-ID.
Signature verificationValidate the HMAC signature on every request.

The recommended pattern is to receive the webhook, enqueue it for asynchronous processing, and respond immediately.

The Guardline architecture is single-tenant. Each integrator receives dedicated backend, database, and frontend instances. There is no sharing of data or application code between instances. The underlying infrastructure (Kubernetes cluster, networking, observability) is shared, but logical isolation is enforced at every layer: separate databases, separate application processes, separate storage, and separate credentials. One integrator’s iframe has no access to another integrator’s data, even indirectly.

The backend applies differentiated security headers based on route type:

Route TypeX-Frame-OptionsCSP frame-ancestorsPermissions-Policy
Embed and onboarding (/embed/*, /onboarding/*)Not setIntegrator’s allowed origins onlycamera=(self "https://integrator.com"), accelerometer=(self "https://integrator.com"), gyroscope=(self "https://integrator.com"), magnetometer=(self "https://integrator.com")
Admin panel (/admin/*)DENY'none'All sensors blocked
API (/api/*)DENY'none'All sensors blocked

The frame-ancestors directive in the Content Security Policy controls which domains can embed the iframe. Only the integrator’s explicitly configured domains are allowed. This is more flexible than the legacy X-Frame-Options header (which only supports DENY or SAMEORIGIN) and is the modern mechanism recommended by all major browsers.

The list of allowed origins is configured per instance. Changes require coordination with the Guardline team.

Session authentication uses HTTP cookies. In a cross-origin iframe context, the browser only sends cookies if they are configured with SameSite=None and Secure=true, which requires HTTPS (mandatory in production).

Browsers with restrictive third-party cookie policies (Safari with ITP, Firefox with ETP, Chrome with privacy controls) may block or partition these cookies. The practical impact is limited because the onboarding session is ephemeral (created and consumed within minutes) and does not depend on persistent cookies across sessions.

For integrators where third-party cookie restrictions are a concern, using a custom domain (CNAME pointing to Guardline infrastructure) converts the iframe cookies to first-party, eliminating these restrictions entirely. Contact the Guardline team for custom domain setup.

Attack VectorMitigation
ClickjackingCSP frame-ancestors restricts which domains can embed the iframe. A malicious site cannot embed the onboarding in a disguised page.
Token replayTokens are bound to a single execution and time-limited (link validity + active session window). A captured token grants access only to the execution it was created for, and is permanently invalidated once the execution reaches a terminal state.
PostMessage XSSOrigin validation on both sides. The iframe only sends messages to the configured origin; the host page must validate the origin of every received message.
Webhook replayHMAC signature with timestamp. Webhooks older than 5 minutes should be rejected by the integrator’s endpoint.
Token enumerationTokens are 32-character hex values from a cryptographically secure source (128 bits of entropy). Brute-force enumeration is computationally infeasible.
Man-in-the-middleHTTPS (TLS 1.2+) is mandatory for all network communication: API calls, iframe loading, and webhook delivery. PostMessage is a browser-internal mechanism between windows in the same user agent and does not traverse the network, so it is not susceptible to network-level interception.

The embedded onboarding is supported on all modern browsers. Camera in cross-origin iframes is supported from Chrome 64+, Firefox 90+, Safari 15+, and Edge 79+. Mobile support includes Chrome (Android) 64+ and Safari (iOS) 15+.

The iframe requires the following allow attributes: camera (required on all platforms), accelerometer, gyroscope, and magnetometer (required on mobile for biometric liveness detection). Without camera, the onboarding cannot proceed past document or biometric steps. Without motion sensors, the biometric provider may function in degraded mode on mobile.

For detailed browser compatibility matrices, legacy device notes (iOS 14), and third-party provider behavior (iProov, Nethone), see Appendix A: Browser Compatibility Details.

The following error codes may be emitted via the PostMessage error event. They represent technical conditions that prevent the onboarding from progressing.

CodeDescriptionSuggested Action
camera_deniedThe user denied camera access when prompted by the browser.Display a message explaining that camera access is required and instruct the user to grant permission in browser settings.
camera_not_foundNo camera was detected on the device.Display a message suggesting the user switch to a device with a camera.
permission_not_grantedThe iframe does not have the required allow attribute for camera. This is a configuration error on the integrator’s side.Verify that the iframe element includes allow="camera".
api_unavailableThe Guardline API is unreachable.Display a generic error message and suggest retrying. If persistent, contact Guardline support.
sdk_load_failedThe biometric verification SDK failed to load (network issue or CDN failure).Display an error message and suggest retrying. The user can attempt the step again.
token_invalidThe session token is malformed or does not exist.Display a message informing the user the link is invalid. The integrator should generate a new session.
token_expiredThe session token has expired.Display a message informing the user the link has expired. The integrator should generate a new session.
session_completedThe execution associated with this token has already reached a final decision.Display a message informing the user the onboarding has already been finalized.
session_blockedThe session was blocked due to security reasons.Display a message and direct the user to support channels.
network_errorA network request failed due to connectivity issues.Display a message suggesting the user check their connection and retry.
SymptomCauseSolution
Iframe is blank or white screenX-Frame-Options: DENY applied globally, overriding CSP frame-ancestors.Verify the Guardline instance omits X-Frame-Options on embed routes. Contact the Guardline team.
PostMessage events not receivedHost page not filtering by source === 'guardline-onboarding', or allowed origin misconfigured.Check the event listener filter. Verify allowed origins match the exact domain of your host page (protocol + domain + port).
Camera does not work in iframeIframe element missing allow="camera".Add allow="camera" to the iframe. On mobile, also include accelerometer; gyroscope; magnetometer.
Biometric fails on mobileMotion sensor permissions not declared on iframe.Add accelerometer; gyroscope; magnetometer to the iframe’s allow attribute.
Cookies not sent in iframeBrowser blocking third-party cookies (Safari ITP, Firefox ETP).Minimal impact for ephemeral sessions. For persistent issues, configure a custom domain (CNAME) for first-party context.
User loses progress after navigating awayHost page destroyed and recreated the iframe, losing sessionStorage.Expected behavior. The backend persists all step data. The session resumes automatically from the last completed step with the same token.
Webhooks not arrivingEndpoint unreachable, returning non-2xx, or signature verification rejecting valid payloads.Check endpoint accessibility via HTTPS. Check logs in the Guardline admin panel. Verify signature is computed over timestamp + "." + body.

Core onboarding data (name, CPF, contact data, document metadata, and document images) is stored in Guardline systems and encrypted at rest. Biometric captures are processed by iProov and are not retained by Guardline. Device fingerprints are processed by Nethone and are not retained by Guardline. The integrator is the data controller under LGPD, Guardline acts as data processor. Retention periods, legal basis, and DPA terms are defined per contract.

For the complete data residency diagram, retention table, and compliance responsibility matrix, see Appendix B: Data Flow and Compliance Details.

A typical KYC flow with 6 steps completes in 3-5 minutes of user interaction. Backend operations (token resolution, step submission, decision engine) are sub-second. The biometric verification step (5-15s) is the most variable, depending on network latency to iProov servers and device camera hardware.

These figures are engineering reference, not SLA commitments. Performance SLAs are defined per contract. For detailed P95 metrics with measurement context, see Appendix C: Performance Details.

Use this checklist to validate your integration before going to production.

  • Session creation endpoint is implemented and returns the token to your frontend.
  • Webhook endpoint is implemented, accessible via HTTPS, and responds with 2xx.
  • Webhook signature verification is implemented using the shared secret.
  • Webhook idempotency is implemented using X-Guardline-Event-ID.
  • Webhook timestamp validation rejects events older than 5 minutes.
  • Session status polling is implemented as a fallback for missed webhooks.
  • API key is stored securely (environment variable, secret manager), not in source code.
  • Iframe element includes allow="camera; accelerometer; gyroscope; magnetometer".
  • Iframe does not include the sandbox attribute.
  • PostMessage listener filters by source === 'guardline-onboarding'.
  • PostMessage origin validation checks the exact Guardline domain (no substring or regex matching).
  • The ready event is handled (hide loading indicator, show iframe).
  • The completed event is handled with branching by decision value: approved (confirmation), rejected (appropriate message), in_review (inform user of pending review).
  • The error event is handled (display user-friendly message).
  • The blocked event is handled (display support information).
  • End-to-end onboarding flow works in Chrome, Safari, and Firefox (desktop).
  • End-to-end onboarding flow works in Chrome (Android) and Safari (iOS 15+).
  • Camera access works inside the iframe on all target browsers.
  • Biometric verification completes successfully inside the iframe.
  • Session resumption works: destroy the iframe, recreate it with the same token, verify the flow resumes from the correct step.
  • Expired token displays the appropriate informative screen.
  • Invalid token displays the appropriate informative screen.
  • Webhook delivery is confirmed in the Guardline admin panel.
  • Webhook signature verification correctly rejects tampered payloads.
  • Allowed origins are configured for your production domain(s) (required for CSP frame-ancestors and PostMessage origin validation).
  • SSL certificates are valid and not expiring soon.
  • API key for production environment is provisioned and stored securely.
  • Webhook shared secret is stored securely.

For sandbox access and testing support, contact the Guardline technical team.

All API errors follow this structure:

{
"error": true,
"message": "description of the error",
"code": "ERROR_CODE",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
CodeMeaning
200Success
400Invalid request (malformed JSON, validation failure)
401Authentication failed (invalid or missing API key)
404Resource not found (execution ID or token does not exist)
409Conflict (execution is in a terminal state, such as approved, rejected, blocked, or expired)
429Rate limited (too many requests)
500Internal server error
  • On 400: fix the request payload. Do not retry.
  • On 401: verify the API key. Do not retry.
  • On 404: verify the execution ID or token. Do not retry.
  • On 409: the execution is in a terminal state (approved, rejected, blocked, or expired). Treat accordingly.
  • On 429: back off and retry after the indicated delay.
  • On 500: retry with exponential backoff (up to 3 attempts).
  • On network timeout: retry. The reference_id idempotency guarantees no duplicate sessions.
MethodPathAuthDescription
POST/api/v1/onboarding/sessionsAPI KeyCreate a new onboarding session
GET/api/v1/onboarding/flowsAPI KeyList available onboarding flows
GET/api/v1/onboarding/executions/{execution_id}API KeyRetrieve execution status and decision
GET/api/v1/onboarding/executionsAPI KeyList executions with pagination
POST/api/v1/onboarding/executions/{execution_id}/resend-representative-linkAPI KeyResend SMS/WhatsApp link to legal representative
GET/api/v1/onboarding/notificationsAPI KeyPoll pending webhook notifications
POST/api/v1/onboarding/notifications/{delivery_id}/ackAPI KeyAcknowledge a webhook notification

BrowserMinimum VersionCamera in IframeNotes
Chrome64+ (2018)YesFull support for Permissions Policy and cross-origin iframe camera access.
Firefox90+ (2021)YesFull support. Enhanced Tracking Protection may affect Nethone device profiling.
Safari15+ (2021)YesFull support. Intelligent Tracking Prevention may affect Nethone device profiling.
Edge79+ (2020)YesChromium-based. Same behavior as Chrome.
BrowserMinimum VersionCamera in IframeNotes
Chrome (Android)64+YesFull support.
Safari (iOS)15+YesFull support for camera in cross-origin iframes.
Safari (iOS)14 and earlierNoDoes not support camera in cross-origin iframes. The biometric provider offers a bridge pattern that opens capture in a new tab and returns the result via PostMessage. These versions represent less than 5% of active iOS devices and are declining.
Samsung Internet12+YesChromium-based. Same behavior as Chrome.
PermissionRequiredPurposeImpact if Missing
cameraYesDocument capture and biometric verification.Onboarding cannot proceed past document or biometric steps. The iframe displays an error message.
accelerometerMobile onlyLiveness detection on mobile devices.Biometric provider may function in degraded mode (no motion detection) or fail on some devices.
gyroscopeMobile onlyLiveness detection on mobile devices.Same as accelerometer.
magnetometerMobile onlyLiveness detection on mobile devices.Same as accelerometer.

The iProov Web SDK (version 5.4.3+) supports operation inside iframes. The SDK communicates with iProov servers using its own tokens obtained from the Guardline backend and does not depend on third-party cookies or shared storage.

On mobile devices, iProov uses motion sensors for liveness detection (verifying the device is being held by a real person). The iframe must declare permissions for accelerometer, gyroscope, and magnetometer. Without these declarations, iProov may function in degraded mode (no motion detection) or fail.

Nethone collects device information (fingerprint, typing patterns, fraud signals) using a JavaScript agent loaded from an external CDN. This agent operates with third-party cookies for cross-session device tracking.

In a cross-origin iframe context, browsers with restrictive privacy policies may limit Nethone’s functionality:

BrowserPolicyImpact
SafariIntelligent Tracking Prevention (ITP)Third-party cookies blocked. Device fingerprint reduced to session-only.
FirefoxEnhanced Tracking Protection (ETP)Third-party cookies blocked in strict mode. Similar impact to Safari.
ChromeUser-configurableThird-party cookies allowed by default. Users can disable them.

The onboarding is designed with graceful degradation for Nethone: if the script fails to load, profiling does not complete within the timeout (5 seconds), or cookies are blocked, the flow continues normally. The risk analysis loses the device intelligence dimension, but all other verifications (biometrics, document verification, bureau validation) function independently.

For scenarios where device profiling is critical, running Nethone in the integrator’s domain (first-party context, no third-party cookie restrictions) and communicating the result to the iframe via PostMessage is a supported alternative. Contact the Guardline team for this configuration.


Data TypeStorage LocationRetentionEncrypted at Rest
Session tokenGuardline database (hashed)Until expiration or completionYes
Personal data (name, CPF, email)Guardline databasePer data retention policy (configurable)Yes
Document imagesGuardline object storagePer data retention policy (configurable)Yes
Biometric dataProcessed by iProov, not stored by GuardlineNot retainedN/A
Device fingerprintProcessed by Nethone, not stored by GuardlineNot retainedN/A
Decision resultsGuardline databaseImmutable audit trailYes
Webhook payloadsGuardline database (delivery log)90 daysYes
AreaWhat Guardline Provides (Technical)Contractual / ConfigurableIntegrator Responsibility
LGPDData is collected with a specific, documented purpose (identity verification). Retention periods are technically enforced by the platform.Retention duration, legal basis for processing, and DPA (Data Processing Agreement) are defined per contract.The integrator is the data controller and must ensure lawful basis for initiating the onboarding (e.g., user consent, contractual necessity). Guardline acts as data processor.
Data residencyThe platform supports deployment in specific geographic regions or on-premise.Region selection and on-premise deployment are subject to contractual agreement.The integrator must define residency requirements based on its own regulatory obligations.
Audit trailAll operations are recorded in an immutable audit log, accessible via the admin panel.Log retention period and export format are configurable per contract.The integrator is responsible for consuming and archiving audit logs as required by its own compliance policies.
Data minimizationOnly data necessary for the configured flow steps is collected.Flow configuration (which steps are active, which fields are required) is defined during setup.The integrator controls which pre-fill data to send. Sending unnecessary personal data in the pre-fill is the integrator’s responsibility.
Right to deletionThe platform supports data deletion requests technically.Deletion procedures and SLAs are defined per contract.The integrator must forward deletion requests from data subjects to Guardline and confirm completion to the requesting party.

The values below are P95 measurements observed in the Europe region under normal load conditions. Actual performance varies depending on the integrator’s infrastructure, end-user network quality, and device capabilities. These figures are provided as engineering reference, not as SLA commitments. Performance SLAs are defined per contract.

MetricP95Measurement Context
Iframe initial load< 2sStandard broadband (10 Mbps+). Includes SPA bundle (gzipped), fonts, and token resolution. Cached assets reduce subsequent loads to < 500ms.
Token resolution< 200msBackend processing time only (database lookup + flow configuration assembly). Does not include network round-trip from the client.
Step submission< 300msBackend processing time only (validation + persistence).
Document capture (camera init)1-3sVaries with device camera hardware and browser. First capture on a page load is slower due to camera permission prompt and hardware initialization.
Biometric verification5-15sEnd-to-end including iProov SDK initialization, capture, and server-side processing. Heavily dependent on network latency to iProov servers.
Decision engine< 500msBackend processing time only. Operates exclusively on data already collected during the onboarding steps. No external provider calls at this stage.
PostMessage delivery< 1msBrowser-internal, same-device. Not a network operation.
Webhook first delivery attempt< 5sTime from decision to HTTP request to the integrator’s endpoint. Does not include the integrator’s processing time.

Factors that affect end-user experience:

  • Network quality: Document upload and biometric verification are the most bandwidth-intensive steps. On connections below 3 Mbps, capture and upload times increase significantly.
  • Device camera quality: Lower-quality cameras may require multiple capture attempts for document verification.
  • Number of steps in the flow: Each step adds user navigation time. A KYC flow with 6 steps typically completes in 3-5 minutes of user interaction.
  • Browser: Modern browsers (Chrome 90+, Safari 16+, Firefox 100+) provide the best performance for camera access and WebGL rendering used by the biometric SDK.