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.
Design Principles
Seção intitulada “Design Principles”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.
Integration Flow
Seção intitulada “Integration Flow”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.
Creating a Session
Seção intitulada “Creating a Session”The starting point of every integration is creating an onboarding session from the integrator’s backend.
Endpoint
Seção intitulada “Endpoint”POST /api/v1/onboarding/sessionsAuthentication
Seção intitulada “Authentication”All API requests must include a valid API key in the X-API-Key header.
X-API-Key: gl_live_a1b2c3d4e5f6g7h8i9j0API 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.
Environments
Seção intitulada “Environments”Each integrator receives a dedicated instance with its own base URL. All API paths in this document are relative to that base URL.
| Environment | Base URL Pattern | API Key Prefix |
|---|---|---|
| Sandbox | https://{instance}.onp.dev.guardline.com.br | gl_test_ |
| Production | https://{instance}.onp.prod.guardline.com.br | gl_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.
Request
Seção intitulada “Request”Content-Type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
flow_type | string | yes | Onboarding flow type: kyc (individuals), kyb (businesses), or kyc_minor (minors aged 14-17 with legal representative). Determines which verification steps are included. |
flow_id | string | no | UUID 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_id | string | no | Your internal identifier for this user or process. Returned in webhooks and queryable via API. |
redirect_url | string | no | URL to redirect the user after completion in direct link mode. Ignored in iframe mode. |
pre_fill | object | no | Known user data for pre-filling form fields (see below). For kyc_minor, birth_date is required and must correspond to age 14-17. |
Pre-fill Fields
Seção intitulada “Pre-fill Fields”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.
| Field | Type | Description |
|---|---|---|
full_name | string | User’s full name |
tax_id | string | CPF (individuals) or CNPJ (businesses) |
email | string | Email address |
phone | string | Phone number with country code |
birth_date | string | Date 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.
Request Example
Seção intitulada “Request Example”{ "flow_type": "kyc", "reference_id": "USR-2026-00042", "pre_fill": { "full_name": "Maria Santos", "tax_id": "123.456.789-00", "email": "maria@example.com" }}Response
Seção intitulada “Response”{ "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" }}| Field | Type | Description |
|---|---|---|
execution_id | UUID | Unique identifier for this onboarding execution. Use it to query status via API. |
token | string | Session 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. |
url | string | Full URL for direct link mode (user opens in browser). |
embed_url | string | Full URL for iframe mode (use as the iframe src). |
expires_at | datetime | ISO 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). |
Token Security
Seção intitulada “Token Security”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.
Token Expiration
Seção intitulada “Token Expiration”Two time windows govern the token lifecycle, and the stricter one always takes precedence:
| Window | Duration | Starts When | Purpose |
|---|---|---|---|
| Link validity | 7 days (default, configurable) | Session is created via API | Gives the user time to open the link. Covers scenarios where the user receives a notification and opens it days later. |
| Active session | 24 hours | User opens the iframe for the first time | Limits 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.
Token in URL: Leakage Mitigations
Seção intitulada “Token in URL: Leakage Mitigations”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 Vector | Mitigation |
|---|---|
| Referer header | The 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 history | The 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 logs | The 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 scripts | The 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 logs | The 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. |
Idempotency
Seção intitulada “Idempotency”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.
Embedding the Iframe
Seção intitulada “Embedding the Iframe”The integrator’s frontend embeds the onboarding by mounting an iframe element pointing to the embed_url returned by the session creation endpoint.
Required Attributes
Seção intitulada “Required Attributes”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.
| Attribute | Value | Purpose |
|---|---|---|
src | embed_url from session creation response | Points to the Guardline onboarding for this session. |
allow | camera; accelerometer; gyroscope; magnetometer | Camera is required for document capture and biometric verification. Motion sensors (accelerometer, gyroscope, magnetometer) are required on mobile devices for liveness detection. |
width | 100% | The onboarding layout is responsive and adapts to the container width. |
height | 100% of viewport or fixed value (minimum 600px) | The layout is designed to occupy the full height of its container. |
Attributes to Avoid
Seção intitulada “Attributes to Avoid”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.
Visual Configuration
Seção intitulada “Visual Configuration”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:
| Setting | Description |
|---|---|
| Primary and secondary colors | In dark and light variants, used for buttons, accents, and interactive elements. |
| Logo URLs | Hosted on CDN, in versions for dark and light backgrounds. |
| Theme mode | Dark, light, or automatic (follows the user’s system preference). |
| Button styling | Background, text, and icon colors per theme. |
| Per-step overrides | Individual 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.
PostMessage Events
Seção intitulada “PostMessage Events”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.
Message Structure
Seção intitulada “Message Structure”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.
Events Reference
Seção intitulada “Events Reference”Emitted once, after the iframe loads, resolves the session token, and is ready to display the first step.
| Payload Field | Type | Description |
|---|---|---|
executionId | UUID | The 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.
step-changed
Seção intitulada “step-changed”Emitted each time the user advances to the next step or navigates back.
| Payload Field | Type | Description |
|---|---|---|
executionId | UUID | The execution identifier. |
stepId | string | The identifier of the current step (e.g., personal-data, document-front, biometric). |
stepIndex | integer | Zero-based index of the current step in the flow. |
totalSteps | integer | Total 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.
completed
Seção intitulada “completed”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 Field | Type | Description |
|---|---|---|
executionId | UUID | The execution identifier. |
decision | string | Immediate 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:
decision | Suggested UX |
|---|---|
approved | Close the iframe, display a success confirmation. |
rejected | Close the iframe, display an appropriate message or redirect to a support flow. |
in_review | Close 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. |
blocked
Seção intitulada “blocked”Emitted when the flow is interrupted for security reasons.
| Payload Field | Type | Description |
|---|---|---|
executionId | UUID | The execution identifier. |
reason | string | The 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 Field | Type | Description |
|---|---|---|
executionId | UUID | The execution identifier. |
code | string | A standardized error code (see Error Reference). |
message | string | A human-readable description of the error. |
height-changed
Seção intitulada “height-changed”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 Field | Type | Description |
|---|---|---|
height | integer | The content height in pixels. |
Origin Validation
Seção intitulada “Origin Validation”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.
Communication from Host to Iframe
Seção intitulada “Communication from Host to Iframe”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.
Session Lifecycle
Seção intitulada “Session Lifecycle”An onboarding session transitions through a defined set of states from creation to final resolution.
| State | Terminal | Description |
|---|---|---|
created | No | Session exists but the user has not started the onboarding yet. |
in_progress | No | User has opened the iframe and is navigating through steps. |
completed | No | All steps have been submitted. The decision engine is processing. This is a transient state. |
approved | Yes | The user has been approved (automatically or by an analyst). |
rejected | Yes | The user has been rejected (automatically or by an analyst). |
in_review | No | The execution has been referred to a human analyst for manual review. |
blocked | Yes | The flow was interrupted during the onboarding due to a security rule (fraud detection, excessive biometric attempts, identity mismatch). |
expired | Yes | The session expired before completion. The token is invalidated. |
Querying Session Status
Seção intitulada “Querying Session Status”Retrieve the current state of an onboarding execution at any time.
GET /api/v1/onboarding/executions/{execution_id}Headers:
X-API-Key: gl_live_a1b2c3d4e5f6g7h8i9j0Response:
{ "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.
Session Resumption
Seção intitulada “Session Resumption”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.
Webhooks
Seção intitulada “Webhooks”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.
| Event | Description |
|---|---|
onboarding.started | The user accessed the link and initiated the onboarding process. |
onboarding.completed | All required steps were submitted and decision processing started. |
onboarding.approved | The user was approved (automatic or manual decision). |
onboarding.rejected | The user was rejected (automatic or manual decision). |
onboarding.review | The execution was referred to a human analyst for review. |
onboarding.blocked | The flow was interrupted due to a security rule (fraud detection, excessive biometric attempts, identity mismatch). Terminal state. |
onboarding.expired | The session expired without completion. |
For kyc_minor flows, three additional events track the legal representative’s progress:
| Event | Description |
|---|---|
representative.pending | The minor completed their part. The system is waiting for the legal representative to start. |
representative.started | The legal representative opened the link and began the verification. |
representative.completed | The legal representative finished all required steps. The final decision follows. |
Event Ordering and Delivery Guarantees
Seção intitulada “Event Ordering and Delivery Guarantees”Events for a given execution follow these emission-order rules:
onboarding.startedis emitted when the user opens the onboarding flow.- If all required steps are submitted,
onboarding.completedis emitted. - After
onboarding.completed, exactly one ofonboarding.approved,onboarding.rejected, oronboarding.reviewis emitted. - If
onboarding.reviewis emitted, exactly one ofonboarding.approvedoronboarding.rejectedfollows when the analyst resolves the case. onboarding.blockedmay be emitted only while the execution isin_progress, and it is terminal. No other events follow it.onboarding.expiredis emitted if the session expires before a terminal decision, and it is terminal. No other events follow it. For sessions never started,onboarding.expiredmay be the only event.- For
kyc_minorflows, afteronboarding.completed,representative.pendingis emitted. When the representative opens the link,representative.startedfollows. When the representative finishes,representative.completedis emitted, followed by the final decision event (onboarding.approved,onboarding.rejected, oronboarding.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).
Payload Format
Seção intitulada “Payload Format”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": {}}| Field | Type | Always Present | Description |
|---|---|---|---|
event | string | yes | The event type (see table above). |
execution_id | UUID | yes | The execution identifier. |
reference_id | string | yes | The integrator’s reference ID, as provided during session creation. |
flow_type | string | yes | The flow type (kyc, kyb, or kyc_minor). |
event_sequence | integer | yes | Monotonically increasing integer per execution. Use this to determine canonical event order (see Event Ordering). |
timestamp | datetime | yes | ISO 8601 timestamp of the event emission. |
data | object | yes | Event-specific payload (see below). |
Webhook Example: onboarding.approved
Seção intitulada “Webhook Example: onboarding.approved”{ "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:
| Event | data.decision.result | data.decision.reason_code | data.decision.reason_description |
|---|---|---|---|
onboarding.approved | approved | null (automatic) or analyst reason | null or analyst description |
onboarding.rejected | rejected | e.g., DOCUMENT_FRAUD_DETECTED | e.g., “Document tampering detected during verification” |
onboarding.review | in_review | e.g., BIOMETRIC_LOW_CONFIDENCE | e.g., “Biometric verification returned low confidence score” |
onboarding.blocked | blocked | e.g., MAX_ATTEMPTS_EXCEEDED | e.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" }}Headers
Seção intitulada “Headers”Every webhook request includes the following HTTP headers:
| Header | Description |
|---|---|
Content-Type | Always application/json |
X-Guardline-Event-ID | Unique UUID for this event. Stable across retries of the same event. Use this for deduplication. |
X-Guardline-Delivery-ID | Unique UUID for this specific delivery attempt. Changes on each retry. Use this for delivery tracking and debugging. |
X-Guardline-Signature | HMAC-SHA256 hex-encoded signature of the request body |
X-Guardline-Timestamp | Unix timestamp of when the signature was computed |
Verifying the Signature
Seção intitulada “Verifying the Signature”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:
- Read the
X-Guardline-Timestampheader. - Verify that the timestamp is within an acceptable window (recommended: 5 minutes).
- Concatenate the timestamp and the raw request body as bytes:
timestamp + "." + body. - Compute
HMAC-SHA256(concatenated_value, shared_secret)and hex-encode the result. - Compare your computed signature with the
X-Guardline-Signatureheader value using a constant-time comparison. - 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.
Retry Policy
Seção intitulada “Retry Policy”If Guardline does not receive an HTTP 2xx response, the webhook is retried with exponential backoff:
| Attempt | Delay After Failure |
|---|---|
| 1 | 5 seconds |
| 2 | 30 seconds |
| 3 | 2 minutes |
| 4 | 10 minutes |
| 5 | 30 minutes |
| 6 | 1 hour |
| 7 | 2 hours |
| 8 | 4 hours |
| 9 | 8 hours |
| 10 | 12 hours |
After 10 failed attempts, the delivery is marked as exhausted and an internal alert is generated. 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.
Idempotency
Seção intitulada “Idempotency”Webhooks may be delivered more than once (due to retries after network issues). Use the X-Guardline-Event-ID header to deduplicate:
- Store each processed
X-Guardline-Event-ID. - On receiving a webhook, check if the event ID was already processed.
- 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.
Endpoint Requirements
Seção intitulada “Endpoint Requirements”The integrator’s webhook endpoint must meet the following requirements:
| Requirement | Description |
|---|---|
| HTTPS | Mandatory. Protects the payload and signature in transit. |
| 2xx response | Return any 2xx status to confirm receipt. |
| 10-second timeout | Respond within 10 seconds. Process asynchronously if needed. |
| Idempotent | Handle duplicate deliveries using X-Guardline-Event-ID. |
| Signature verification | Validate the HMAC signature on every request. |
The recommended pattern is to receive the webhook, enqueue it for asynchronous processing, and respond immediately.
Security
Seção intitulada “Security”Instance Isolation
Seção intitulada “Instance Isolation”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.
HTTP Security Headers
Seção intitulada “HTTP Security Headers”The backend applies differentiated security headers based on route type:
| Route Type | X-Frame-Options | CSP frame-ancestors | Permissions-Policy |
|---|---|---|---|
Embed and onboarding (/embed/*, /onboarding/*) | Not set | Integrator’s allowed origins only | camera=(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.
Cross-Origin Cookies
Seção intitulada “Cross-Origin Cookies”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 Protection
Seção intitulada “Attack Protection”| Attack Vector | Mitigation |
|---|---|
| Clickjacking | CSP frame-ancestors restricts which domains can embed the iframe. A malicious site cannot embed the onboarding in a disguised page. |
| Token replay | Tokens 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 XSS | Origin 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 replay | HMAC signature with timestamp. Webhooks older than 5 minutes should be rejected by the integrator’s endpoint. |
| Token enumeration | Tokens are 32-character hex values from a cryptographically secure source (128 bits of entropy). Brute-force enumeration is computationally infeasible. |
| Man-in-the-middle | HTTPS (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. |
Browser Compatibility
Seção intitulada “Browser Compatibility”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.
Error Reference
Seção intitulada “Error Reference”The following error codes may be emitted via the PostMessage error event. They represent technical conditions that prevent the onboarding from progressing.
| Code | Description | Suggested Action |
|---|---|---|
camera_denied | The 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_found | No camera was detected on the device. | Display a message suggesting the user switch to a device with a camera. |
permission_not_granted | The 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_unavailable | The Guardline API is unreachable. | Display a generic error message and suggest retrying. If persistent, contact Guardline support. |
sdk_load_failed | The 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_invalid | The 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_expired | The session token has expired. | Display a message informing the user the link has expired. The integrator should generate a new session. |
session_completed | The execution associated with this token has already reached a final decision. | Display a message informing the user the onboarding has already been finalized. |
session_blocked | The session was blocked due to security reasons. | Display a message and direct the user to support channels. |
network_error | A network request failed due to connectivity issues. | Display a message suggesting the user check their connection and retry. |
Troubleshooting
Seção intitulada “Troubleshooting”| Symptom | Cause | Solution |
|---|---|---|
| Iframe is blank or white screen | X-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 received | Host 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 iframe | Iframe element missing allow="camera". | Add allow="camera" to the iframe. On mobile, also include accelerometer; gyroscope; magnetometer. |
| Biometric fails on mobile | Motion sensor permissions not declared on iframe. | Add accelerometer; gyroscope; magnetometer to the iframe’s allow attribute. |
| Cookies not sent in iframe | Browser 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 away | Host 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 arriving | Endpoint 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. |
Data Flow and Privacy
Seção intitulada “Data Flow and Privacy”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.
Performance
Seção intitulada “Performance”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.
Integration Checklist
Seção intitulada “Integration Checklist”Use this checklist to validate your integration before going to production.
Backend Integration
Seção intitulada “Backend Integration”- 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.
Frontend Integration
Seção intitulada “Frontend Integration”- Iframe element includes
allow="camera; accelerometer; gyroscope; magnetometer". - Iframe does not include the
sandboxattribute. - PostMessage listener filters by
source === 'guardline-onboarding'. - PostMessage origin validation checks the exact Guardline domain (no substring or regex matching).
- The
readyevent is handled (hide loading indicator, show iframe). - The
completedevent is handled with branching bydecisionvalue:approved(confirmation),rejected(appropriate message),in_review(inform user of pending review). - The
errorevent is handled (display user-friendly message). - The
blockedevent is handled (display support information).
Testing
Seção intitulada “Testing”- 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.
Environment
Seção intitulada “Environment”- Allowed origins are configured for your production domain(s) (required for CSP
frame-ancestorsand 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.
Error Handling
Seção intitulada “Error Handling”Error Response Format
Seção intitulada “Error Response Format”All API errors follow this structure:
{ "error": true, "message": "description of the error", "code": "ERROR_CODE", "request_id": "550e8400-e29b-41d4-a716-446655440000"}HTTP Status Codes
Seção intitulada “HTTP Status Codes”| Code | Meaning |
|---|---|
| 200 | Success |
| 400 | Invalid request (malformed JSON, validation failure) |
| 401 | Authentication failed (invalid or missing API key) |
| 404 | Resource not found (execution ID or token does not exist) |
| 409 | Conflict (execution is in a terminal state, such as approved, rejected, blocked, or expired) |
| 429 | Rate limited (too many requests) |
| 500 | Internal server error |
Recommended Error Handling
Seção intitulada “Recommended Error Handling”- 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_ididempotency guarantees no duplicate sessions.
Endpoint Summary
Seção intitulada “Endpoint Summary”| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/v1/onboarding/sessions | API Key | Create a new onboarding session |
| GET | /api/v1/onboarding/flows | API Key | List available onboarding flows |
| GET | /api/v1/onboarding/executions/{execution_id} | API Key | Retrieve execution status and decision |
| GET | /api/v1/onboarding/executions | API Key | List executions with pagination |
| POST | /api/v1/onboarding/executions/{execution_id}/resend-representative-link | API Key | Resend SMS/WhatsApp link to legal representative |
| GET | /api/v1/onboarding/notifications | API Key | Poll pending webhook notifications |
| POST | /api/v1/onboarding/notifications/{delivery_id}/ack | API Key | Acknowledge a webhook notification |
Appendix A: Browser Compatibility Details
Seção intitulada “Appendix A: Browser Compatibility Details”Desktop Browsers
Seção intitulada “Desktop Browsers”| Browser | Minimum Version | Camera in Iframe | Notes |
|---|---|---|---|
| Chrome | 64+ (2018) | Yes | Full support for Permissions Policy and cross-origin iframe camera access. |
| Firefox | 90+ (2021) | Yes | Full support. Enhanced Tracking Protection may affect Nethone device profiling. |
| Safari | 15+ (2021) | Yes | Full support. Intelligent Tracking Prevention may affect Nethone device profiling. |
| Edge | 79+ (2020) | Yes | Chromium-based. Same behavior as Chrome. |
Mobile Browsers
Seção intitulada “Mobile Browsers”| Browser | Minimum Version | Camera in Iframe | Notes |
|---|---|---|---|
| Chrome (Android) | 64+ | Yes | Full support. |
| Safari (iOS) | 15+ | Yes | Full support for camera in cross-origin iframes. |
| Safari (iOS) | 14 and earlier | No | Does 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 Internet | 12+ | Yes | Chromium-based. Same behavior as Chrome. |
Iframe Permissions Detail
Seção intitulada “Iframe Permissions Detail”| Permission | Required | Purpose | Impact if Missing |
|---|---|---|---|
camera | Yes | Document capture and biometric verification. | Onboarding cannot proceed past document or biometric steps. The iframe displays an error message. |
accelerometer | Mobile only | Liveness detection on mobile devices. | Biometric provider may function in degraded mode (no motion detection) or fail on some devices. |
gyroscope | Mobile only | Liveness detection on mobile devices. | Same as accelerometer. |
magnetometer | Mobile only | Liveness detection on mobile devices. | Same as accelerometer. |
Biometric Verification (iProov)
Seção intitulada “Biometric Verification (iProov)”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.
Device Profiling (Nethone)
Seção intitulada “Device Profiling (Nethone)”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:
| Browser | Policy | Impact |
|---|---|---|
| Safari | Intelligent Tracking Prevention (ITP) | Third-party cookies blocked. Device fingerprint reduced to session-only. |
| Firefox | Enhanced Tracking Protection (ETP) | Third-party cookies blocked in strict mode. Similar impact to Safari. |
| Chrome | User-configurable | Third-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.
Appendix B: Data Flow and Compliance Details
Seção intitulada “Appendix B: Data Flow and Compliance Details”Where Data Resides
Seção intitulada “Where Data Resides”Data Retention
Seção intitulada “Data Retention”| Data Type | Storage Location | Retention | Encrypted at Rest |
|---|---|---|---|
| Session token | Guardline database (hashed) | Until expiration or completion | Yes |
| Personal data (name, CPF, email) | Guardline database | Per data retention policy (configurable) | Yes |
| Document images | Guardline object storage | Per data retention policy (configurable) | Yes |
| Biometric data | Processed by iProov, not stored by Guardline | Not retained | N/A |
| Device fingerprint | Processed by Nethone, not stored by Guardline | Not retained | N/A |
| Decision results | Guardline database | Immutable audit trail | Yes |
| Webhook payloads | Guardline database (delivery log) | 90 days | Yes |
Compliance Responsibility Matrix
Seção intitulada “Compliance Responsibility Matrix”| Area | What Guardline Provides (Technical) | Contractual / Configurable | Integrator Responsibility |
|---|---|---|---|
| LGPD | Data 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 residency | The 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 trail | All 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 minimization | Only 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 deletion | The 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. |
Appendix C: Performance Details
Seção intitulada “Appendix C: Performance Details”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.
| Metric | P95 | Measurement Context |
|---|---|---|
| Iframe initial load | < 2s | Standard broadband (10 Mbps+). Includes SPA bundle (gzipped), fonts, and token resolution. Cached assets reduce subsequent loads to < 500ms. |
| Token resolution | < 200ms | Backend processing time only (database lookup + flow configuration assembly). Does not include network round-trip from the client. |
| Step submission | < 300ms | Backend processing time only (validation + persistence). |
| Document capture (camera init) | 1-3s | Varies with device camera hardware and browser. First capture on a page load is slower due to camera permission prompt and hardware initialization. |
| Biometric verification | 5-15s | End-to-end including iProov SDK initialization, capture, and server-side processing. Heavily dependent on network latency to iProov servers. |
| Decision engine | < 500ms | Backend processing time only. Operates exclusively on data already collected during the onboarding steps. No external provider calls at this stage. |
| PostMessage delivery | < 1ms | Browser-internal, same-device. Not a network operation. |
| Webhook first delivery attempt | < 5s | Time 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.