ID Pass is now available on iOS and Android.Discover ID Pass

HLID API / DOCUMENTATION

API documentation

Verify individuals and companies through HeliumID's hosted flows. Create sessions from your server, send people to a secure link, and receive results by webhook or through the API.

Base URLhttps://server.heliumid.io/api
API v1
Start in test mode. Create a Test API key in the HeliumID Dashboard. Test sessions are never billed, and you can trigger decisions yourself.

Getting started

#Introduction

HeliumID verifies your end-users and business customers through secure, hosted verification flows. Your server creates a verification session, your user completes it on any device, and HeliumID sends the result to your systems.

HeliumID exposes two verification families:

  • Individual verifications via POST /v1/verifications: document capture, selfie and biometric matching for a single person.
  • Company verifications via POST /v1/company-verifications: business registration details, company documents, ownership information and checks on the authorised representative.

Core steps

  1. Sign in. Go to HeliumID and sign in. If you don't have an account yet, one is created for you.
  2. Create an API key. Open the Developers page and create a key.
  3. Choose your integration. Call the API from your server to create sessions, or create sessions manually from the dashboard.
  4. Create a verification session. Generate a new session for your end-user or business customer.
  5. Receive verified data. If you have set up webhooks on your API key, you receive the session result and verified data automatically.
  6. Review the session. If needed, review the session manually in the HeliumID Dashboard.

Individual verification in a nutshell

  1. Create a verification session. Call POST /v1/verifications. You receive a hosted link and a verification ID.
  2. Complete verification. The end-user completes the hosted verification flow.
  3. Receive webhooks or fetch status. Wait for webhook notifications, or call GET /v1/verifications/{id} to fetch the current status and user data.

Company verification in a nutshell

  1. Create a company verification. Call POST /v1/company-verifications. You receive a hosted link, a public verification ID and an expiry timestamp.
  2. The representative completes the hosted flow. They upload company documents, review extracted data and submit.
  3. Track progress. Follow company webhook events, the dashboard, or GET /v1/company-verifications/{id}.

#Prerequisites

Before you send your first request, make sure you:

  1. Have access to the HeliumID Dashboard.
  2. Have created an API key.
  3. Have configured the webhook URLs for that API key, if you want automated updates.
  4. Have your API keys and the base URL at hand.
Important: Make every API request from your server. Never expose your API key in browser or mobile-app code.

#Authentication

Every endpoint is authenticated with an API key sent in the x-api-key request header.

To get your key:

  1. Sign in to the HeliumID Dashboard.
  2. Open the Developers page from the left navigation bar.
  3. Generate or copy an active API key.

Base URL

Send every request to the base URL, followed by the endpoint path:

text
https://server.heliumid.io/api/v1/{endpoint}

Request headers

HeaderRequiredDescription
x-api-keyAlwaysYour integration's API key. Identifies and authenticates the sender.
Content-TypePOST and PATCHSet to application/json.

Requests with a missing or invalid key return 401 Unauthorized.

Warning: Your API key also signs the webhooks HeliumID sends you. Store it in an environment variable or secrets manager and never commit it to source control.

#API keys

You use API keys to exchange data with HeliumID. There are two types: test keys and live keys.

Test keys

Use test keys for development. Verification sessions completed with a test key don't count towards your paid usage.

HeliumID doesn't make automatic decisions on test sessions. You trigger the decisions yourself from the dashboard so you can exercise every outcome in your integration.

Warning: Stress testing without prior agreement with HeliumID is not allowed.

Live keys

Use live keys in production. A live key requires a working webhook URL. Sessions completed with a live key count towards your paid usage, and HeliumID makes the decisions.

Create an API key

  1. Sign in to your workspace.
  2. Open Developers in the left-hand menu.
  3. Click Add API Key.
  4. Give the key a suitable name.
  5. Choose the key type: Test or Live.

Webhook URLs

Each API key can have two webhook destinations: an events webhook URL for progress updates and a decision webhook URL for final outcomes. See Webhooks.

Test integrations

If you have an active HeliumID account, one test integration is created for you. Use it to test communication between your system and HeliumID and to see how to handle responses. You can create more test integrations if you need them; sessions created with them are never billed.

#Quick start

Create your first individual verification with a test key.

1. Create a test API key

In the dashboard, open Developers › Add API Key and choose Test. Add your webhook URLs if you want progress and decision updates.

2. Create a verification

Call POST /v1/verifications from your server. Pass your own user ID as vendorData so you can match the result to your record.

Redirect the user to data.link exactly as returned. Store data.verificationId alongside your user record.

4. Handle the result

Listen for verification.successful and verification.failed on your decision webhook, or fetch the session with GET /v1/verifications/{id}. With a test key, set the decision yourself from the dashboard.

Note: Create endpoints return HTTP 200, not 201. The JSON body echoes the same value in its code field.

#Test vs live

Test and live keys use the same integration shape. What changes is who controls the final decision and whether usage is billed.

BehaviourTest API keyLive API key
Intended useDevelopment, QA and integration testingProduction
BillingDoesn't count towards paid usageCounts towards paid usage
WebhooksSupportedSupported
HMAC signingSupportedSupported
Customer-admin testing controlsAvailable where supportedNot available for terminal decisions

Individual verifications

CapabilityTestLive
Create verification sessionsYesYes
Receive progress webhook eventsYesYes
Receive terminal decision webhook eventsYesYes
Manually update terminal status from the dashboardYesNo
Use for real production decisionsNoYes

Company verifications

CapabilityTestLive
Create company verification sessionsYesYes
Receive company progress webhook eventsYesYes
Receive company decision webhook eventsYesYes
Move a company verification into review from the dashboardYesNo
Set terminal company decisions from the dashboardYesNo
Final decision controlled by HeliumIDNot required for testingYes

Workspace admins should not rely on live dashboard controls for terminal company outcomes: live company verifications are reviewed and decided by HeliumID.

Keep these identical in both environments

  • Request format
  • Webhook signature verification
  • Event parsing
  • Retry behaviour
  • Storage of vendorData, verification IDs and event logs

Tell your team

Make sure engineering, operations and support know which key environment is in use, whether a decision was test- or live-generated, and whether a company outcome came from HeliumID review or customer-admin testing.

#Testing your integration

Use these checklists before you switch to a live key.

Functional checklist

  • A new session link is generated.
  • Session data is saved with your record.
  • The end-user gets access to your platform after an approved decision.
  • The end-user is told about a failed verification after a declined decision.
  • Both positive and negative terminal webhook outcomes are handled.
  • At the end of verification, the user is redirected back to the right place in your platform.

Security checklist

  • Your server rejects a webhook signed with the wrong API key.
  • Your server rejects a webhook with a mismatched signature.
  • Your server doesn't crash when it receives a webhook with invalid JSON.
  1. Build your integration against a test API key.
  2. Validate webhook signatures and payload parsing.
  3. Test both positive and negative decision handling.
  4. Verify dashboard review behaviour.
  5. Move to a live key once your webhook and session handling is stable.

Guides

#Individual verification

An individual verification session is the standard way to send an end-user into HeliumID's hosted identity flow.

Create a session with the API

  1. Send the verification object to https://server.heliumid.io/api/v1/verifications.
  2. Include the Content-Type: application/json header.
  3. Include the x-api-key header with your integration's API key.
  4. Include vendorData. It is strongly recommended so you can identify the session on your side.
  5. Optionally include meta to pass additional custom data.
  6. Optionally set method to auto (the default, automated systems) or manual (reviewed by an Identity Specialist).
  7. Read data.link from the response. You need it to bring the end-user into the verification flow.

What to do with the response

  1. Store data.verificationId on your side.
  2. Redirect the end-user to data.link.
  3. Wait for webhooks, or fetch the session later with GET /v1/verifications or GET /v1/verifications/{id}.

How updates are delivered

Progress events go to your events webhook URL: verification.started, verification.selfie_uploaded, verification.document_front_uploaded, verification.document_back_uploaded, verification.processing_started and verification.biometrics_matched.

Terminal outcomes go to your decision webhook URL: verification.successful and verification.failed.

If you prefer to poll, use the fetch endpoints.

Create a session manually

You can also create a session in the dashboard. Use this to test or debug sessions, or as a manual fallback. It creates a link you can share with the end-user.

  1. Open the HeliumID Dashboard.
  2. Click Verifications in the left-hand menu.
  3. Click Create Verification.
  4. Copy the generated link and share it.

Session creation limits

The number of sessions you can create in an integration is capped according to your HeliumID plan. Contact support if you need a higher limit.

Other languages

go
package main

import (
  "fmt"
  "io"
  "net/http"
  "strings"
)

func main() {
  url := "https://server.heliumid.io/api/v1/verifications"
  payload := strings.NewReader(`{"vendorData":"user_12345","method":"auto","meta":{"key":"value"}}`)

  req, _ := http.NewRequest("POST", url, payload)
  req.Header.Add("Content-Type", "application/json")
  req.Header.Add("x-api-key", "YOUR_API_KEY")

  res, _ := http.DefaultClient.Do(req)
  defer res.Body.Close()
  body, _ := io.ReadAll(res.Body)

  fmt.Println(string(body))
}
java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
  public static void main(String[] args) throws Exception {
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://server.heliumid.io/api/v1/verifications"))
        .header("Content-Type", "application/json")
        .header("x-api-key", "YOUR_API_KEY")
        .method("POST", HttpRequest.BodyPublishers.ofString("{\"vendorData\":\"user_12345\",\"method\":\"auto\",\"meta\":{\"key\":\"value\"}}"))
        .build();

    HttpResponse<String> response = HttpClient.newHttpClient()
        .send(request, HttpResponse.BodyHandlers.ofString());

    System.out.println(response.body());
  }
}

#Individual decisions

When an individual verification reaches a terminal outcome, HeliumID sends a decision webhook to the API key's decision webhook URL. The terminal decision events are verification.successful and verification.failed.

Ways to get a decision

  • Dashboard: the Verifications page in the HeliumID Dashboard.
  • Decision webhook: configure a webhook URL on your API key to receive automated updates.
  • API: call GET /v1/verifications/{id} to fetch the current status.

What a decision means

StatusMeaning
successfulHeliumID verified the user.
failedHeliumID could not verify the user.
cancelledThe verification was cancelled before it completed.

You may also see in-progress states such as not started and started if you fetch a session before it reaches an outcome. Some records and internal tools also show compatibility states such as completed or abandoned. See Verification statuses.

Session lifecycle

  1. Created. You create a session through the API or the dashboard.
  2. Started. The user enters the flow and provides document images and a selfie.
  3. Processed. HeliumID evaluates the submitted data.
  4. Decision reached. The session becomes successful or failed.

For richer progress signals before the final outcome, configure your events webhook URL as well. Events such as verification.started and verification.processing_started are sent there.

Note: To give a user another attempt after a conclusive outcome, create a new session with POST /v1/verifications.

#Company verification

Company verification collects business registration details, company documents, ownership information and checks on the authorised representative through HeliumID's hosted company flow.

Before you start

  • Create an API key in the HeliumID Dashboard.
  • Make the request from your server. Never expose your API key in browser or mobile-app code.
  • Configure your API key's webhook URLs if you want automated updates.
  • Keep the returned hosted link intact. It contains a secure hosted-flow token and must be used exactly as returned.

Understand the response

The response contains two identifiers with different purposes:

  • data.link is the full hosted URL to send the representative to.
  • data.verificationId is HeliumID's public company verification ID, useful for your own tracking and webhook correlation.

The hosted URL also contains processId (HeliumID's internal company verification record ID) and hostedToken (the secure token that authorises the hosted session). See IDs and terminology.

Send the representative to the hosted flow

Redirect the company's authorised representative to data.link. The hosted page shows your workspace name and logo in its Requested by card.

The representative completes these steps:

  1. Upload company documents.
  2. Review AI-assisted extraction results.
  3. Confirm company details.
  4. Review ownership and control details.
  5. Complete the authorising-person section.
  6. Provide additional compliance information.
  7. Review and submit the company verification.

Depending on your workspace configuration, the flow can also include a hosted selfie liveness check for the authorising person.

  • Don't replace processId with vendorData, your own customer ID or the public verificationId.
  • Don't strip hostedToken from the URL.
  • Don't generate your own company verification links on the client.

To resend the representative to the flow, reuse the link you stored or generate a new hosted link from the dashboard.

Track progress

Track company verifications with company webhook events, the dashboard, or GET /v1/company-verifications/{id}.

Progress events go to the events webhook URL: company_verification.submitted, company_verification.liveness_completed and company_verification.under_review. Decisions go to the decision webhook URL: company_verification.successful and company_verification.failed.

Live and test company verifications

  • Live: HeliumID handles the final review and terminal decision.
  • Test: customer admins can move the verification through review states in the dashboard.

#Company lifecycle

A company verification typically moves through the flow like this:

  1. You create a company verification with POST /v1/company-verifications.
  2. The representative opens the hosted link.
  3. The representative uploads documents, reviews extracted data and submits the flow.
  4. HeliumID reviews the submission.
  5. HeliumID issues a final outcome.

Statuses

StatusWhat it means
not startedCreated, but the representative hasn't started the hosted flow yet.
startedThe representative has entered the flow and is working through the steps.
submittedThe representative completed the hosted flow and submitted it for review.
under reviewA reviewer is handling the submission.
successfulHeliumID approved the company verification. Terminal.
failedHeliumID rejected the company verification. Terminal.

In practice, submitted means the representative has finished their part, under review means a reviewer is handling the case, and successful and failed are the outcomes to treat as final.

Webhook events

Progress events, sent to the events webhook URL, tell you work is moving forward but are not final outcomes:

  • company_verification.submitted
  • company_verification.liveness_completed
  • company_verification.under_review

Terminal events are sent to the decision webhook URL:

  • company_verification.successful
  • company_verification.failed

Payload shape

Company events include a normalised companyProfile block and a review block, so downstream systems such as bank-account or business-customer providers can consume company details in a stable shape. company_verification.successful uses the same shape as company_verification.submitted; the event, status and review values change to reflect the outcome.

Profile fields may come from data entered in the hosted flow, document extraction and normalisation, or internal review enrichment. The review block tells you how ready the payload is, especially when nested fields still depend on review or further evidence. See Company payloads.

What to store

  • Your own business or application ID, sent as vendorData
  • The verificationId returned by the create response
  • The webhook data.companyProfile, if you provision downstream business accounts
  • The webhook event
  • The webhook data.status
Note: Treat live and test traffic the same way in your webhook handling, but make sure your business logic knows whether the source API key is live or test.

#Reviewing in the dashboard

The HeliumID Dashboard is the main place to inspect a full verification record and the webhooks sent for it.

Individual verifications

  1. Sign in to the HeliumID Dashboard.
  2. Open Verifications in the left navigation bar.
  3. Search by vendor data or verification ID, or use the filters.
  4. Open the verification.

You can review the verification status, extracted document data, uploaded media, the decision reason for failed sessions, and the environment and API key context.

Company verifications

  1. Sign in to the HeliumID Dashboard.
  2. Open Verifications.
  3. Switch to the company verifications view.
  4. Search by company name, vendorData or company verification ID.
  5. Open the record.

The detail view can include:

  • Current verification status
  • Company profile information
  • Representative and authorising-person details
  • Uploaded company documents
  • AI-assisted extracted company data
  • Ownership and beneficial-owner information
  • Additional compliance information
  • The redirect URL and webhook URL stored on the verification
  • Campaign metadata, when the verification was created from a company verification campaign

Webhook payloads and deliveries

  1. Open Events in the dashboard.
  2. Find the relevant verification. or company_verification. event.
  3. Open the event to inspect the exact payload HeliumID sent.
  4. Switch to the Webhooks view to inspect delivery attempts, retries and response failures.

This is useful to confirm which event was emitted, what JSON was sent and whether delivery succeeded, and to debug sandbox or test-key integrations.

Review permissions

  • Live company verifications: HeliumID makes the final review and terminal decision internally.
  • Test company verifications: workspace admins can move the verification into review states and terminal outcomes to test the full integration loop.

#Recipe: individual integration

The simplest reliable pattern for individual verification.

What you'll build

  1. Create a verification from your server.
  2. Store the returned verificationId.
  3. Redirect the user to the hosted link.
  4. Receive webhook updates.
  5. Mark the user as approved or rejected when a decision arrives.

Step 1: Create the verification

Call POST /v1/verifications and store data.verificationId, data.link and your own vendorData.

Step 2: Redirect the user

Send the user to data.link exactly as returned. Don't rebuild the hosted URL yourself.

Step 3: Listen for webhooks

At minimum, handle verification.successful and verification.failed. You can also listen for progress events such as verification.started and verification.processing_started.

Step 4: Verify the signature

Validate the Webhook-Timestamp and Webhook-Signature headers against the raw request body. See Signature verification.

Step 5: Update your system

  • On verification.successful, mark the user as verified.
  • On verification.failed, mark the user as rejected or request a new session.

Optional polling fallback

If you need a fallback, fetch the current state with GET /v1/verifications or GET /v1/verifications/{id}.

  • verificationId
  • vendorData
  • Current status
  • Latest webhook event
  • Raw webhook payload
  • Environment used

#Recipe: company integration

The recommended pattern for company verification.

What you'll build

  1. Create a company verification from your server.
  2. Store the public company verification ID.
  3. Send the representative to the hosted link.
  4. Handle progress and decision webhooks.
  5. Reconcile the result in your own system.

Step 1: Create the company verification

Call POST /v1/company-verifications and store data.verificationId (the public company verification ID), data.link and your own vendorData.

Step 2: Send the representative to the hosted flow

Use data.link exactly as returned. It contains processId (the internal company verification ID) and hostedToken (secure access to the hosted flow). Don't rebuild it.

Step 3: Listen for company webhooks

Progress events are company_verification.submitted, company_verification.liveness_completed and company_verification.under_review. Decision events are company_verification.successful and company_verification.failed.

Step 4: Reconcile IDs correctly

  • Create-response verificationId is the public company verification ID.
  • Webhook companyVerificationId is the internal company verification ID.
  • Webhook externalId is the public company verification ID.

For most customer-facing systems, externalId is the most useful long-term reference.

Step 5: Apply review logic

  • On company_verification.submitted, mark the case as awaiting review.
  • On company_verification.under_review, mark the case as in review.
  • On company_verification.successful, mark the business as approved.
  • On company_verification.failed, mark the business as rejected and store the reason.
  • Public verificationId from the create response
  • Internal companyVerificationId, when it first arrives by webhook
  • externalId
  • vendorData
  • Current status
  • Latest webhook event
  • Raw webhook payload
  • Environment used

In test, customer admins can move company verifications through review and terminal states. In live, HeliumID controls the final decision. See Test vs live.

API reference

#Responses and errors

Every response uses a consistent JSON envelope. The HTTP status code is in the response line and the same number is echoed in the JSON code field. The business payload is inside data.

Success envelope

json
{
  "status": true,
  "message": "Created verification",
  "data": {},
  "code": 200
}

Error envelope

Some validation errors also include an error object or extra metadata, depending on the endpoint.

json
{
  "status": false,
  "message": "Verification not found",
  "code": 404
}

HTTP status codes

CodeDescriptionWhy you might see it
200OKSuccessful requests, including the current create endpoints.
201CreatedMay be used by resource-creation endpoints. Don't assume every create returns 201; check each endpoint.
400Bad RequestThe payload, parameters or business rules are invalid. For example a malformed URL, an invalid verification ID, or an update in an unsupported state.
401UnauthorizedAuthentication failed: a missing or invalid API key, or an invalid hosted-flow token.
404Not FoundThe requested resource can't be found.
500Internal Server ErrorSomething went wrong on HeliumID's side.

Practical notes

  • Treat any non-2xx response as a failed call, even if the body contains more detail.
  • Don't rely on 201 for create operations. The verification and company verification create endpoints return 200.
  • Read the JSON message field for a human-readable explanation when a request fails.

#Pagination

List endpoints are paginated with page numbers. Pass page (starting at 1) and limit (default 10) as query parameters. Each list response includes a meta object.

FieldTypeDescription
totalintegerTotal number of records.
pageintegerThe current page.
limitintegerRecords per page.
totalPagesintegerTotal number of pages.

Keep requesting the next page until page equals totalPages.

#List verifications

GET/v1/verifications

Fetches the individual verifications created by the current API key. Results are paginated and don't include deleted records. Each item is a verification object.

Query parameters

pageintegerOptional
Page number, starting at 1. Default 1.
limitintegerOptional
Maximum number of records per page. Minimum 1, default 10.

#Create a verification

POST/v1/verifications

Creates an individual verification session and returns a hosted verification link for the end-user. The request body is optional, but sending vendorData is strongly recommended.

Body parameters

vendorDatastringOptional
Your own correlation identifier, such as your user ID. Echoed back in webhooks and verification records.
metaobjectOptional
Additional custom data to store with the verification. Stored as userMeta.
methodstringOptional
auto (default) uses automated systems. manual routes the session to review by an Identity Specialist.

Store data.verificationId and redirect the user to data.link exactly as returned. For individual verifications, verificationId is the same value as the processId in the hosted link.

#Retrieve a verification

GET/v1/verifications/{id}

Fetches the current state of a single individual verification created by the current API key. Returns a verification object.

Path parameters

idstringRequired
The verification ID returned when the session was created.

If you get 404 Not Found, check that you are using an individual verification ID (not a company verification ID), that the verification belongs to the same API key, and that it hasn't been deleted.

#Cancel a verification

PATCH/v1/verifications/{id}/cancel

Cancels an individual verification that hasn't yet reached a terminal successful or failed outcome. The session moves to cancelled (5000) and the user can't continue with it.

Path parameters

idstringRequired
The verification ID returned when the session was created.

Cancelling a verification that is already terminal returns 400 Bad Request.

#Delete a verification

DELETE/v1/verifications/{id}

Soft-deletes an individual verification. Deleted records no longer appear in fetch results for the current API key.

Path parameters

idstringRequired
The verification ID returned when the session was created.

#The verification object

Returned by the individual verification fetch and cancel endpoints.

FieldTypeDescription
idstringThe verification ID. Same value as verificationId and the hosted-link processId.
statusstringOne of not started, started, abandoned, completed, successful, failed, cancelled. See Verification statuses.
statusCodeintegerNumeric counterpart of status, for example 2010.
reasonstringFailure reason, or an empty string.
vendorDatastringYour correlation identifier.
userMetaobjectThe meta object you sent when creating the session.
userEmailstringThe end-user's email, when available.
verificationMethodstringauto or manual.
selfieobjectSelfie storage key and url.
docobjectDocument type, name, issuingState, and key and url objects with front and back.
docInfoobjectData extracted from the document. See below.
createdAtstringISO 8601 creation time.
updatedAtstringISO 8601 time of the last update.

docInfo

FieldTypeDescription
docTypestringDocument type, for example NATIONAL_ID or PASSPORT.
docNumberstringDocument number.
issueDatestringIssue date, if available.
expiryDatestringExpiry date, if available.
issuingStatestringIssuing country or state code.
firstNamestringFirst name.
lastNamestringLast name.
middleNamestringMiddle name.
dobstringDate of birth.
sexstringSex, as printed on the document.

#List company verifications

GET/v1/company-verifications

Fetches the company verifications created by the current API key. Results are paginated and ordered by most recently updated. Each item is a company verification object.

Query parameters

pageintegerOptional
Page number, starting at 1. Default 1.
limitintegerOptional
Maximum number of records per page. Minimum 1, default 10.

#Create a company verification

POST/v1/company-verifications

Creates a company verification session and returns a hosted link for the authorised representative. The returned verificationId is the public company verification ID. The processId inside the link is the internal company verification record ID.

Body parameters

vendorDatastringOptional
Your own identifier, such as a business or application ID.
metaobjectOptional
Additional metadata to store with the company verification.
campaignIdstringOptional
A company verification campaign ID, in the format cmp_ followed by 24 hex characters. When set, the session uses that campaign's published configuration.

The response also includes campaignVersion (the published campaign version used) and expiresAt (when the hosted link expires). Treat data.link as opaque: never rebuild it or remove its query parameters. See Company verification.

#Retrieve a company verification

GET/v1/company-verifications/{id}

Fetches the current state of a single company verification created by the current API key. Returns a company verification object.

Path parameters

idstringRequired
Either the internal company verification ID or the public company verification ID returned as verificationId when the session was created.

#Delete a company verification

DELETE/v1/company-verifications/{id}

Deletes a company verification created by the current API key.

Path parameters

idstringRequired
Either the internal company verification ID or the public company verification ID returned as verificationId.

#The company verification object

Returned by the company verification fetch endpoints. Fields other than the identifiers and status are only present once the relevant data has been collected.

FieldTypeDescription
idstringInternal company verification ID. Same value as the hosted-link processId and the webhook companyVerificationId.
verificationIdstringPublic company verification ID. Same value as externalId.
externalIdstringPublic company verification ID, for example cv_….
statusstringOne of not started, started, submitted, under review, abandoned, successful, failed.
reasonstringRejection reason, or an empty string.
vendorDatastringYour correlation identifier.
userMetaobjectThe meta object you sent when creating the session.
redirectstringThe redirect URL stored on the verification.
webhookUrlstringThe webhook URL stored on the verification.
campaignIdstringCampaign ID, if created from a campaign.
campaignVersionintegerPublished campaign version used.
businessProfileobjectBusiness details, such as companyName and countryOfRegistration.
representativeProfileobjectRepresentative details, such as legalFirstName and legalSurname.
documentsobjectNamed document slots.
uploadedDocumentsarrayUploaded document descriptors.
extractedProfileobjectAI-assisted data extracted from documents.
ownershipProfileobjectShareholders, directors and beneficial owners.
authorityProfileobjectAuthority declarations and liveness status, such as livenessStatus and livenessVerificationId.
additionalInformationobjectAdditional compliance information.
analysisobjectDocument analysis state.
progressobjectFlow progress, such as currentStep and submittedAt.
auditTrailarrayAudit entries for the verification.
customQuestionAnswersarrayAnswers to campaign custom questions.
createdAtstringISO 8601 creation time.
updatedAtstringISO 8601 time of the last update.
Note: Webhooks carry a normalised companyProfile instead of these raw profile objects. Use company webhook payloads when you provision downstream accounts.

Webhooks

#Overview

HeliumID uses webhooks to notify your integration when verification progress changes or a final outcome is reached. Every webhook uses the same outer JSON shape: event tells you what happened and data holds the payload.

json
{
  "event": "verification.successful",
  "data": {}
}

Destinations

Each API key can be configured with two webhook destinations:

  • Events webhook URL: progress updates that keep your UI or internal workflow in sync.
  • Decision webhook URL: final outcomes, when a verification reaches a terminal result.

If you only care about final outcomes, implement the decision webhook first. You can listen to it alone.

Headers

HeliumID signs every webhook with the Webhook-Timestamp and Webhook-Signature headers. See Signature verification.

Handling webhooks reliably

  • Respond with a 2xx status quickly.
  • Verify the signature against the raw request body before you trust the payload.
  • Ignore unknown fields and unfamiliar event types without throwing errors.
  • Store the raw payload for auditing and replay.

What to store

  • The webhook event
  • The webhook data
  • Your own correlation key, vendorData
  • The HeliumID verification ID
  • The API key environment used to create the verification

#Individual events

These events apply to sessions created with POST /v1/verifications.

EventDestinationTerminalMeaning
verification.startedEventsNoThe end-user entered the hosted verification flow.
verification.selfie_uploadedEventsNoThe end-user uploaded a selfie.
verification.document_front_uploadedEventsNoThe end-user uploaded the front of their document.
verification.document_back_uploadedEventsNoThe end-user uploaded the back of their document.
verification.processing_startedEventsNoHeliumID started evaluating the submitted data.
verification.biometrics_matchedEventsNoThe selfie matched the face on the document.
verification.successfulDecisionYesThe verification was approved.
verification.failedDecisionYesThe verification was rejected.

See Individual payloads for every field.

#Company events

These events apply to sessions created with POST /v1/company-verifications.

EventDestinationTerminalMeaning
company_verification.submittedEventsNoThe representative completed and submitted the hosted flow.
company_verification.liveness_completedEventsNoThe authorising-person liveness step completed.
company_verification.under_reviewEventsNoThe company verification moved into review.
company_verification.successfulDecisionYesThe company verification was approved.
company_verification.failedDecisionYesThe company verification was rejected.

Company events include a normalised companyProfile block and a review block, so systems such as banking or account-opening providers can consume business, contact, officer, ownership and authority details without depending on internal form structures. These objects are part of the payload contract; what can vary is how complete the nested fields are at a given moment.

company_verification.successful has the same shape as the submitted example. The event name, status and review values reflect the approved state. See Company payloads.

#Signature verification

HeliumID signs every webhook with an HMAC-SHA256 signature in the Webhook-Signature header, so you can confirm the payload came from HeliumID and wasn't tampered with. Your API key is the signing secret.

The signature payload

The signed string is the timestamp and the raw JSON body joined by a period:

text
{timestamp}.{payload}
  • timestamp is the UNIX epoch time in milliseconds, taken exactly from the Webhook-Timestamp header.
  • payload is the exact JSON body of the webhook, containing event and data.

Verify a signature

  1. Read the raw request body before any JSON parsing.
  2. Build the signature payload as {timestamp}.{rawBody}.
  3. Compute an HMAC-SHA256 hex digest using your API key as the secret.
  4. Compare it to Webhook-Signature with a constant-time comparison.

Best practices

  • Hash the raw body. Re-serialising a parsed object can change whitespace or key order and break the signature.
  • Prevent timing attacks. Use crypto.timingSafeEqual in Node.js, hmac.compare_digest in Python or subtle.ConstantTimeCompare in Go.
  • Reject replays. Compare the timestamp with your server's clock and reject requests outside a tolerance window, such as 5 minutes.
  • Protect your API key. Keep it in environment variables, never in client-side code.

Go example

go
package main

import (
  "crypto/hmac"
  "crypto/sha256"
  "crypto/subtle"
  "encoding/hex"
  "io"
  "net/http"
  "os"
)

func heliumWebhook(w http.ResponseWriter, r *http.Request) {
  rawBody, _ := io.ReadAll(r.Body)
  timestamp := r.Header.Get("Webhook-Timestamp")
  received := r.Header.Get("Webhook-Signature")

  mac := hmac.New(sha256.New, []byte(os.Getenv("HELIUM_ID_API_KEY")))
  mac.Write([]byte(timestamp + "." + string(rawBody)))
  expected := hex.EncodeToString(mac.Sum(nil))

  if subtle.ConstantTimeCompare([]byte(expected), []byte(received)) != 1 {
    http.Error(w, "invalid signature", http.StatusUnauthorized)
    return
  }

  // Parse rawBody and handle the event
  w.WriteHeader(http.StatusOK)
}

#Individual payloads

Field-by-field reference for webhooks sent for sessions created with POST /v1/verifications.

verification.started

FieldTypeRequiredNotes
verificationIdstringYesInternal verification record ID.
statusstringYesCurrent status, started.
statusCodenumberYesNumeric status code, 2000.
vendorDatastringNoYour correlation value, if supplied when the session was created.

verification.selfie_uploaded

FieldTypeRequiredNotes
verificationIdstringYesInternal verification record ID.
selfieKeystringNoStorage key for the uploaded selfie.
vendorDatastringNoYour correlation value, if supplied.

verification.document_front_uploaded

FieldTypeRequiredNotes
verificationIdstringYesInternal verification record ID.
documentTypestringYesDocument type selected in the flow.
issuingStatestringYesDocument issuing country or state code.
vendorDatastringNoYour correlation value, if supplied.

verification.document_back_uploaded

FieldTypeRequiredNotes
verificationIdstringYesInternal verification record ID.
documentTypestringYesDocument type stored on the verification.
vendorDatastringNoYour correlation value, if supplied.

verification.processing_started

FieldTypeRequiredNotes
verificationIdstringYesInternal verification record ID.
vendorDatastringNoYour correlation value, if supplied.

verification.biometrics_matched

FieldTypeRequiredNotes
verificationIdstringYesInternal verification record ID.
vendorDatastringNoYour correlation value, if supplied.

verification.successful

FieldTypeRequiredNotes
verificationIdstringYesInternal verification record ID.
vendorDatastringNoYour correlation value, if supplied.
statusCodenumberYesNumeric decision code, 2010.
docInfoobjectYesParsed document information. See below.
userSelfiestringNoURL of the stored selfie image.
userIdBackstringNoURL of the back document image, when available.
userIdFrontstringNoURL of the front document image.

docInfo

FieldTypeRequiredNotes
docTypestringYesDocument type.
docNumberstringNoExtracted document number.
issueDatestringNoExtracted issue date, if available.
expiryDatestringNoExtracted expiry date, if available.
issuingStatestringNoExtracted issuing country or state.
firstNamestringNoExtracted first name.
lastNamestringNoExtracted last name.
middleNamestringNoExtracted middle name.
dobstringNoExtracted date of birth.
sexstringNoExtracted sex value.

verification.failed

FieldTypeRequiredNotes
verificationIdstringYesInternal verification record ID.
reasonstringYesFailure reason stored on the verification.
vendorDatastringNoYour correlation value, if supplied.
statusCodenumberYesNumeric decision code, 4100.

#Company payloads

Field-by-field reference for webhooks sent for sessions created with POST /v1/company-verifications.

Shared company fields

Most company events include this common shape.

FieldTypeRequiredNotes
companyVerificationIdstringYesInternal company verification record ID.
verificationIdstringYesPublic company verification ID. Matches the verificationId returned by the create response.
externalIdstringYesPublic company verification ID, for example cv_….
vendorDatastringNoYour correlation value, if supplied.
statusstringYesCurrent company verification status.
reasonstringNoRejection reason, or an empty string.
submittedAtstringNoISO timestamp of submission, when available.
campaignIdstringNoCampaign ID, if created from a campaign.
campaignVersionnumberNoCampaign version, if available.
companyProfileobjectYesNormalised company data for downstream integrations such as account-opening and banking providers. Nested fields may be empty if the data hasn't been collected or confirmed yet.
reviewobjectYesReadiness and review metadata explaining how complete or review-dependent the profile is.

Events

EventPayload
company_verification.submittedShared fields, with companyProfile and review.
company_verification.under_reviewShared fields.
company_verification.successfulShared fields, with companyProfile and review. Same shape as submitted, with values describing the successful outcome.
company_verification.failedShared fields. reason contains the rejection reason.
company_verification.liveness_completedShared fields, plus livenessVerificationId (string, required: the linked individual verification used for liveness) and userSelfie (string: URL of the selfie captured during liveness).

companyProfile.business

FieldTypeNotes
legalNamestringCompany legal name.
tradingNamestringTrading or operating name, when available.
registrationNumberstringCompany registration number, for example an RC number.
registrationTypestringBusiness or registration type.
countryOfRegistrationstringCountry of incorporation or registration.
dateOfRegistrationstringRegistration date, when available.
businessBvnstringBusiness-level BVN, when explicitly collected or confirmed. Distinct from officers' personal BVNs.
taxIdstringTax identifier or TIN, where available.
industrystringIndustry or sector.
descriptionstringBusiness activity or description.
websitestringBusiness website.

companyProfile.contact

FieldTypeNotes
emails.generalstringPrimary company email.
emails.alternatestringAlternate company email, when available.
emails.representativestringRepresentative email.
phoneNumbers.generalstringPrimary business phone number.
phoneNumbers.alternatestringAlternate business phone number.
phoneNumbers.representativestringRepresentative phone number.
addresses.operatingobjectOperating address.
addresses.registeredobjectRegistered address.
addresses.representativeResidentialobjectRepresentative residential address, when captured.

companyProfile.authorisedRepresentative

FieldTypeNotes
firstNamestringFirst name.
middleNamestringMiddle name.
lastNamestringSurname.
fullNamestringFull name.
emailstringEmail.
phoneNumberstringPhone number.
nationalitystringNationality.
countryOfResidencestringCountry of residence.
relationshipToBusinessstringRole or relationship to the business.
isAuthorisedstringWhether the representative states they are authorised.
ownsPartOfBusinessstringWhether the representative states they own part of the business.
shareholdingPercentagestringShareholding percentage, when declared.
identityDocumentTypestringID document type.
documentNumberstringID document number.
issuingCountrystringID issuing country.
expiryDatestringID expiry date.
dateOfBirthstringDate of birth.
residentialAddressobjectResidential address, or the best available address string.

companyProfile.officers

An array. Each officer can include:

FieldTypeNotes
firstNamestringFirst name.
middleNamestringMiddle name.
lastNamestringSurname.
fullNamestringFull name.
emailstringEmail.
phoneNumberstringPhone number.
rolestringNormalised role, such as director, owner or authorised_representative.
rolesstring[]Additional normalised roles, when applicable.
rawRolestringOriginal self-declared role text, where available.
nationalitystringNationality.
countryOfResidencestringCountry of residence.
shareholdingPercentagestringOwnership share or declared allocation.
identityDocumentTypestringID document type.
documentNumberstringID document number.
issuingCountrystringID issuing country.
expiryDatestringID expiry date.
dateOfBirthstringDate of birth.
bvnstringBVN, when captured.
addressobjectBest available structured or full-text address.
sourcestringSource of the officer row, for example self_reported.

companyProfile.ownership

FieldTypeNotes
thresholdnumberBeneficial ownership threshold used for derivation.
shareholdersobject[]Shareholder rows from the ownership profile.
directorsobject[]Director rows from the ownership profile.
beneficialOwnersobject[]Beneficial owners derived from the ownership profile.
unresolvedCorporateOwnersstring[]Material corporate owners that may still need deeper beneficial-owner look-through.

companyProfile.authority

FieldTypeNotes
rolestringRole declared in the authority section.
isAuthorisedstringWhether authority has been declared.
isDirectorstringWhether the representative is a director.
additionalSignatoryRequiredstringWhether extra authority evidence is needed.
livenessStatusstringCurrent liveness status.
livenessVerificationIdstringLinked individual liveness verification ID.
livenessStartedAtstringLiveness start timestamp.
livenessCompletedAtstringLiveness completion timestamp.

companyProfile.additionalInformation

FieldTypeNotes
purposeOfRelationshipstringDeclared purpose of the relationship.
expectedActivitystringExpected activity summary.
expectedTransactionVolumestringExpected transaction volume.
sourceOfFundsstringSource of funds.
sourceOfWealthstringSource of wealth.
countriesOfOperationstring[]Countries of operation.
regulatoryStatusstringRegulatory status, when provided.
regulatoryLicencesstringRegulatory licence information.

companyProfile.documents

FieldTypeNotes
providedobjectNamed document slots, such as CAC, MEMART, Form 1 or proof of address.
uploadedobject[]Uploaded document descriptors, including file name, type, key, URL and upload time where available.

review

Tells downstream consumers whether the payload is ready for provisioning or still needs verification or review.

FieldTypeNotes
sourcestringHigh-level source of the current profile: self_reported, document_enriched or mixed.
analysisStatusstringCurrent document-analysis status.
currentStagestringCurrent analysis stage, when available.
providerstringAnalysis provider name, when available.
pendingInternalReviewbooleanWhether the company verification is still awaiting review work.
missingFieldsstring[]Important fields still missing from the normalised profile.
reviewFlagsstring[]Flags raised by normalisation or document analysis.
followUpobjectFollow-up guidance, such as missing evidence, blocking fields or requested documents.
provisioningobjectQuick readiness summary for downstream provisioning. See below.

review.provisioning

FieldTypeNotes
readybooleantrue when the profile is considered ready for downstream provisioning.
pendingFieldsstring[]Important business, representative or officer data still missing.
Note: All companyProfile and review fields are optional. HeliumID may add new optional fields over time, so ignore unknown fields safely.

Reference

#Verification statuses

Individual and company verifications have different lifecycles. status is a string and statusCode is its numeric counterpart.

Individual verifications

These statuses appear in the decision webhook, GET /v1/verifications and GET /v1/verifications/{id}.

StatusCodeSent viaMeaning
not started1001APIA session was created. The end-user hasn't entered the flow yet.
started2000Webhook, APIThe end-user started the session. No decision yet.
abandoned3033Webhook, APIThe end-user started but never finished. A session is abandoned 7 days after creation unless it reaches a conclusive decision first. Complete.
completed2001Webhook, APIThe end-user's required data was submitted and processed.
successful2010Webhook, APIPositive: the end-user was verified. Complete.
failed4100Webhook, APINegative: the end-user wasn't verified, either a fraud case or another severe reason. Complete. Investigate the session further.
cancelled5000Webhook, APIThe session was explicitly cancelled before it completed.

2010, 4100, 3033 and 5000 are conclusive. Once a session reaches one of them it is closed. To give the user another try, create a new session with POST /v1/verifications.

Expiry and abandonment

All individual verification sessions expire after 7 days:

  • Sessions still not started move to cancelled.
  • Sessions still started move to abandoned.

Company verifications

These statuses appear in company webhook payloads, the company verification fetch endpoints and the dashboard.

StatusCodeDelivered throughMeaning
not started1001Dashboard, APICreated, but the representative hasn't started the hosted flow.
started2002Dashboard, APIThe representative started the hosted flow.
submitted2003Webhook, dashboard, APIThe representative completed and submitted the flow for review.
under review2004Webhook, dashboard, APIThe submission is being reviewed.
abandoned3033Dashboard, APIDefined on the model, but not part of the main public lifecycle.
successful2060Webhook, dashboard, APIPositive: HeliumID approved the company verification.
failed4004Webhook, dashboard, APINegative: HeliumID rejected the company verification.

The most important company milestones for integrations are submitted, under review, successful and failed. Live company verifications are decided by HeliumID; test company verifications can be moved through review states from the dashboard.

#IDs and terminology

HeliumID uses a few identifiers that sound similar but serve different purposes. The biggest difference is between individual and company flows.

Individual verifications

verificationId is the main verification record ID. It appears in the POST /v1/verifications response, the GET /v1/verifications/{id} path, individual webhook payloads, and the hosted link as processId. For individual verifications, verificationId and processId are the same value.

text
https://verify.heliumid.io/?processId=69d4f7657bcbe3b58b96b2f7

Company verifications

Company verifications use two identifiers on purpose:

  • Public ID: the verificationId returned by POST /v1/company-verifications, for example cv_0b5f6f4e0aab25d8c130ab21. Stored as externalId. Use it for customer-facing references, webhook correlation and support workflows.
  • Internal ID: companyVerificationId in webhooks. It is also the processId in the hosted link.
text
https://verify.heliumid.io/company?processId=69d4f7657bcbe3b58b96b2f7&hostedToken=...

Other terms

  • externalId: the public company verification ID. Returned as verificationId on create and included in company webhooks. Not used as the hosted processId.
  • vendorData: your own correlation value, such as a user, business, application or onboarding case ID. Stored and echoed back in webhooks and records. Use it as your primary correlation key.
  • meta: extra structured information attached on create, stored as userMeta. Use it for context, workflow hints and integration metadata, not as your only correlation key.
  • campaignId and campaignVersion: the company verification campaign and the published version used to create the session. Returned on create and echoed in webhooks when present.
  • hostedToken: authorises access to the hosted company flow. Treat the full link as opaque, store or forward it exactly as returned, and never remove the token.

Quick mapping

TermFlowMeaning
verificationIdIndividualMain verification record ID and hosted processId
verificationIdCompanyPublic company verification ID, also called externalId
companyVerificationIdCompanyInternal company verification record ID
processIdIndividualVerification ID used by the hosted link
processIdCompanyInternal company verification ID used by the hosted link
externalIdCompanyPublic company verification ID
vendorDataBothYour own correlation identifier
metaBothAdditional custom metadata
campaignIdCompanyCampaign identifier
campaignVersionCompanyCampaign version used
hostedTokenCompanySecure hosted-flow token

For individual verifications, store verificationId, vendorData and the environment used.

For company verifications, store the public verificationId from the create response, the internal companyVerificationId when it first arrives by webhook, vendorData and the environment used.

#Backwards compatibility

HeliumID considers the following changes backwards compatible. Build your systems to handle them gracefully.

  • Adding new properties (strings, objects, arrays) to existing API responses.
  • Changing the order of properties in existing API responses.
  • Adding new API resources, such as new endpoints.
  • Adding new optional request parameters to existing endpoints.
  • Changing the length or format of opaque strings, such as error messages and object IDs.
  • Adding new event types to webhooks.
  • Adding new properties to webhook payloads.
Important: Your webhook listeners must handle unfamiliar event types without throwing errors.

Property order is not static. The order in your actual responses may differ from the order shown in this documentation.

Opaque strings are values your system should store or pass on, not interpret or parse for logic.

#Troubleshooting

I'm not receiving webhooks

  1. Confirm the API key has the correct events and decision webhook URLs.
  2. Confirm your endpoint is publicly reachable.
  3. Confirm your server returns a 2xx response quickly.
  4. Confirm you are listening for the right event names.
  5. Confirm you are testing with the expected API key environment.

In the dashboard, open Events to see whether the event was created, and Webhooks to inspect delivery attempts and failures. If the event exists but wasn't delivered, the issue is usually the webhook URL or the receiving server.

The webhook signature doesn't validate

Common causes are using the wrong API key as the secret, hashing a parsed JSON object instead of the raw body, missing the exact Webhook-Timestamp value, or changing whitespace or key order before hashing. Follow the steps in Signature verification.

My company verification is stuck in started

The representative began the hosted flow but hasn't submitted it. Check whether all required company documents were uploaded, all required sections were completed, and whether the authorising-person liveness step is still pending. Once submitted, the verification moves to submitted and emits company_verification.submitted.

My company verification is submitted but has no decision

That is expected until review completes. The case may be waiting for review or not yet moved to under review. If you are testing, you may not have advanced the decision in the test dashboard yet. Live company verifications are decided by HeliumID.

For individual verifications, confirm you are using the exact data.link returned by the create endpoint.

For company verifications, confirm the URL still contains both processId and hostedToken, that the link hasn't expired, and that you didn't rebuild it. If the link has expired, generate a new one from the dashboard.

GET /v1/verifications/{id} returns not found

Check that you are using an individual verification ID rather than a company verification ID, that the verification belongs to the same API key, and that it hasn't been deleted. Fetch company verifications with GET /v1/company-verifications/{id}.

I can update a test company verification but not a live one

That is expected. Customer-admin review and terminal status updates are limited to company verifications created with a test key. Live terminal decisions are reserved for HeliumID's internal review.

I'm seeing the wrong ID in webhooks

Individual webhooks use verificationId. Company webhooks use companyVerificationId for the internal record and externalId for the public ID. See IDs and terminology.

Events webhook or decision webhook?

Use the events webhook for progress updates and the decision webhook for terminal approval or rejection. If you only care about final outcomes, implement the decision webhook first.

Support checklist

When you contact support about an integration issue, include:

  • The API key environment used
  • The verification type: individual or company
  • Your vendorData
  • The HeliumID verification ID
  • The event name
  • The raw webhook payload
  • The response status your server returned to the webhook

#Changelog

Notable API, webhook and lifecycle changes that might affect your webhook consumers, status handling or dashboard workflows.

18 August 2026

Company verification webhook events. Company verification now uses dedicated event names: company_verification.submitted, company_verification.liveness_completed, company_verification.under_review, company_verification.successful and company_verification.failed.

Expanded company webhook payloads. Company webhooks now include a normalised companyProfile block and a review block covering business registration, contact details, the authorised representative, officers and ownership, and provisioning readiness.

Company lifecycle clarified. The documented lifecycle is not started, started, submitted, under review, successful and failed.

Test and live decisions clarified. Test company verifications can be moved through review and terminal states from the dashboard. Live company verifications keep final decisions under HeliumID's control.

Company IDs clarified. The documentation now distinguishes the public verificationId, the internal companyVerificationId, the hosted-flow processId and the public externalId.

Create responses clarified. Verification create endpoints return HTTP 200 with a JSON body that also includes code: 200.

Notes for integrators

  • Treat webhook payloads as extensible. New optional fields and event types may be added.
  • Handle unknown event names gracefully.
  • Store raw webhook payloads for replay and audit.