Request data with Signa ID

Ask a Signa ID owner to release verified identity data to your business, then exchange the code on your server.

A data release lets your business ask a person for verified data from their Signa ID. The person approves the request in a popup on your page, and your server then gets the data from Blaaiz. The person does not upload documents again.

Read the Signa ID overview first.

Before you start

  • Signa ID data release must be enabled for your business. Contact your account manager.
  • Your OAuth credentials must carry the signa-id:release scope.
  • Register a kyc_url webhook to get the signa_id.grant.revoked event. See Signa webhooks.
  • Install the Signa web SDK on your page.

No scope bundle contains signa-id:release, full-access included. Select the scope by name when you create or rotate your credentials. The release endpoints accept only an OAuth access token. A call with an API key fails with 403.

Flow

Step 1: Create a release request

On your server, create the request:

curl -X POST https://api-prod.blaaiz.com/api/external/signa-id/release-requests \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "idempotency_key": "release-user_10482-2026-10-05",
    "purpose": "Open your trading account",
    "scopes": ["identity", "id_document", "document_images"],
    "origin": "https://yourapp.com",
    "reference": "user_10482"
  }'
FieldRequiredDescription
idempotency_keyYesYour key for this request, 1 to 191 characters.
purposeYesWhy you ask for the data, 3 to 200 characters. The person sees this text before they approve.
scopesYesThe data blocks that you ask for. One to four different values from the table below.
originYesThe exact origin of the page that opens the popup, such as https://yourapp.com.
referenceNoYour own reference for the person, 1 to 191 characters. If you omit it, Blaaiz uses the idempotency_key.
ScopeData block
identityThe first, middle and last name, the date of birth, and the nationality.
id_documentThe type, number, issuing country, issue date and expiry date of the identity document.
addressThe verified address, with the country.
document_imagesThe images of the identity document.

Ask only for the scopes that you need. The person sees each item before they approve.

origin must be the value of window.location.origin on your page: https, a lower-case host, and no path, query or default port. The popup sends the result only to this origin. In the sandbox, http://localhost with a port is also accepted.

{
  "message": "Signa ID release request created.",
  "data": {
    "id": "b84c0427-231c-44c2-b397-e528a775962f",
    "status": "PENDING",
    "purpose": "Open your trading account",
    "scopes": ["document_images", "id_document", "identity"],
    "origin": "https://yourapp.com",
    "reference": "user_10482",
    "expires_at": "2026-10-05T10:44:22.000Z",
    "released_at": null,
    "exchanged_at": null,
    "access": null,
    "created_at": "2026-10-05T10:14:22.000Z",
    "request_token": "EHoPu_CY9bReov_nzsaGcPeex9QqDGx2AQ72MQbFPMo",
    "release_url": "https://id.blaaiz.com/r/EHoPu_CY9bReov_nzsaGcPeex9QqDGx2AQ72MQbFPMo"
  }
}

Store data.id against your own record of the person. Send data.request_token to your page.

The request expires 30 minutes after the create. Blaaiz sends no webhook when a request expires. A release request does not count against your open verification session cap.

release_url works only when the web SDK opens it. Do not send it to the person by email or SMS. Use signa.requestData() on your page.

Idempotency

  • A repeat call with the same idempotency_key and the same values returns the same release request.
  • A repeat call with the same key and different values fails with 422.
  • Verification sessions and release requests share one set of keys. A key that a verification session uses fails with 422 on a release request.

Step 2: Open the popup

Call signa.requestData() from the click handler of a button. A browser blocks a popup that does not come from a user action.

import { createSigna } from '@blaaiz/signa-web-sdk';

const signa = createSigna({ environment: 'production' });

button.onclick = async () => {
  try {
    const { releaseId, code } = await signa.requestData({ requestToken });
    // Send releaseId and code to your server at once.
  } catch (error) {
    // error.code is 'closed', 'blocked' or 'in_progress'.
  }
};

In the popup, the person:

  1. Signs in to Signa ID with an email code or a passkey.
  2. Reviews what you ask for: your business name, the host of your page, your purpose, the data, and the access period of 30 days.
  3. Selects Share, or Decline.
  4. Takes a new selfie. Blaaiz checks that a live person is present and that the face matches the verified identity.

After the selfie, the popup sends releaseId and code to your page and closes.

When requestData() rejects, read error.code:

error.codeCause
closedThe popup closed with no release.
blockedThe browser blocked the popup. Call requestData() from a click handler.
in_progressAnother Signa popup is open on the page.

The page cannot know why the popup closed with no release. The person can decline, their Signa ID can be under review, or their Signa ID cannot cover your request. All of these reject with closed.

To try again, read the release on your server. While its status is PENDING, call requestData() again with the same token. For any other status, create a new release request.

A requestToken that is not 43 base64url characters rejects at once. That error has no code.

Step 3: Exchange the code

Send code to your server at once. The code works one time only, for 5 minutes, and only for your business.

curl -X POST https://api-prod.blaaiz.com/api/external/signa-id/releases/exchange \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "EthamT0KfpkGpItLjuYmLuo8-_PfnkFV1sVo7yVNz70"
  }'
{
  "message": "Signa ID data released.",
  "data": {
    "release": {
      "id": "b84c0427-231c-44c2-b397-e528a775962f",
      "status": "EXCHANGED",
      "purpose": "Open your trading account",
      "scopes": ["document_images", "id_document", "identity"],
      "origin": "https://yourapp.com",
      "reference": "user_10482",
      "expires_at": "2026-10-05T10:44:22.000Z",
      "released_at": "2026-10-05T10:19:47.000Z",
      "exchanged_at": "2026-10-05T10:20:03.000Z",
      "access": {
        "status": "ACTIVE",
        "expires_at": "2026-11-04T10:19:47.000Z"
      },
      "created_at": "2026-10-05T10:14:22.000Z"
    },
    "data": {
      "verification": {
        "status": "VERIFIED",
        "verified_at": "2026-09-12T14:03:51.000Z",
        "released_at": "2026-10-05T10:19:47.000Z",
        "liveness": "PASSED",
        "face_match": "PASSED"
      },
      "identity": {
        "first_name": "Amara",
        "middle_name": null,
        "last_name": "Okafor",
        "date_of_birth": "1993-04-17",
        "nationality": "NGA"
      },
      "id_document": {
        "type": "PASSPORT",
        "number": "A05729314",
        "issuing_country": "NGA",
        "issue_date": "2021-06-02",
        "expiry_date": "2031-06-01"
      },
      "document_images": [
        {
          "id": "doc:901",
          "document_type": "PASSPORT",
          "document_side": "FRONT_SIDE",
          "content_type": "image/jpeg",
          "available": true,
          "unavailable_reason": null
        }
      ]
    }
  }
}

Check that data.release.id is the release that you created for this person in step 1. Do not trust a releaseId that your page sends to your server.

  • data.verification is always present. It tells you when the person verified, and that the liveness check and the face match passed.
  • A data block is present only for a scope that you asked for. A block is null when Blaaiz holds no value for it.
  • id_document.number can be null when Blaaiz cannot read it at the time of the call. Read the release again later.

If the code expires before the exchange, the release stays RELEASED and you cannot read the data. Create a new release request.

The exchange fails with 422 and the message The code is invalid or has expired. when the code is wrong, used, expired, or for a different business.

Step 4: Read the data again

For 30 days after the release, you can read the release and its data again:

curl -X GET https://api-prod.blaaiz.com/api/external/signa-id/releases/b84c0427-231c-44c2-b397-e528a775962f \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

The response has the same shape as the exchange response. data.data is null before the exchange, and when access.status is not ACTIVE.

Step 5: Download the document images

When the release includes document_images, get a download URL for one image with its id:

curl -X GET https://api-prod.blaaiz.com/api/external/signa-id/releases/b84c0427-231c-44c2-b397-e528a775962f/documents/doc:901 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{
  "message": "Document URL generated.",
  "data": {
    "url": "https://storage.example.com/releases/b84c0427/doc-901.jpg?expires=900&signature=EXAMPLE",
    "content_type": "image/jpeg",
    "expires_at": "2026-10-05T10:35:03.000Z"
  }
}

Send an HTTP GET to data.url with no Authorization header. The URL is valid for 15 minutes. Download the image at once, and do not store or share the URL.

The call returns 404 when the release has no document_images scope, when you did not exchange the code, or when access.status is not ACTIVE.

Statuses

statusMeaning
PENDINGThe person did not finish in the popup yet.
RELEASEDThe person approved, and Blaaiz issued the code.
EXCHANGEDYour server exchanged the code.
DECLINEDThe person declined.
EXPIREDThe request expired before the person finished.

access is null before the release. After the release, access.status tells you if you can read the data:

access.statusMeaning
ACTIVEYou can read the data until access.expires_at.
REVOKEDThe person or Blaaiz stopped your access.
EXPIREDThe 30-day access period ended.
UNAVAILABLEBlaaiz cannot release the data at this time.

When access stops

When the person or Blaaiz stops your access, Blaaiz sends signa_id.grant.revoked to your kyc_url:

{
  "event": "signa_id.grant.revoked",
  "data": {
    "release_id": "b84c0427-231c-44c2-b397-e528a775962f",
    "revoked_at": "2026-10-12T08:41:16.000Z"
  }
}

Blaaiz sends no webhook when the 30-day access period ends, or when access becomes UNAVAILABLE. Read access.status before you use the data.

Limits

EndpointLimit for each business
Create a release request30 requests each minute
Exchange a release code30 requests each minute
Get a release document URL30 requests each minute and 600 each hour, shared with the session document URL endpoint

Above a limit, the call returns 429 with a Retry-After header.

Security

  • Call the release endpoints from your server only. Every response carries Cache-Control: no-store.
  • Send the request_token only to the page that opens the popup. Do not put it in a URL or a log.
  • Do not send the OAuth access token of your server to a browser.
  • Do not send the Cross-Origin-Opener-Policy: same-origin header on the page. Use same-origin-allow-popups, or send no header.
  • Do not write the released data or the download URLs to your logs.

On this page