# Callbacks

Callbacks let DIDWW send verification-related HTTP requests to your server. The currently
supported callback is the synchronous **verification request callback**, which lets your
backend approve or reject a verification before the challenge is delivered.

Configure a **callback URL** on your OTP application in the User Panel to receive callbacks.

---

## Verification request callback

The verification request callback lets your backend decide whether a verification can
proceed. When the callback applies to a start request, the Verification API sends a request
to your configured callback endpoint before delivering the challenge.

If your callback endpoint approves the request, delivery proceeds. If it denies the request
or does not return a usable response, the verification is denied and nothing is delivered.

Because the callback is synchronous, the [start
endpoint](api-reference/start-verification.html#otp-verification-api-start) does not return until your callback endpoint
responds or the
callback request times out.

### When it is sent

Whether the callback is sent depends on the application's callback URL and the
[authentication mode](authentication.html#otp-verification-authentication) of the start request:

| Auth mode | Callback URL set? | Behavior |
| --- | --- | --- |
| `public` | Yes | Callback is **sent**. Your server decides. |
| `public` | No | The verification start is **denied** because `public` authentication requires a callback URL. |
| `basic` | Yes | Callback is **sent**. Your server decides. |
| `basic` | No | Callback is **skipped**; the verification proceeds. |
| `application` | Any | Callback is **skipped**. The signed request already proves it came from your trusted server. |

### Request from DIDWW

To receive this request, configure the callback URL on the OTP application before starting a
verification. Your application does not call the callback URL directly. When an eligible start
request reaches the Verification API, the API pauses the verification flow and sends an HTTP
`POST` to the configured callback URL before delivering the challenge.

This happens when the start request uses `public` or `basic` authentication and the OTP
application has a callback URL, as described in [When it is sent](#when-it-is-sent). The Verification API waits
for your callback endpoint to approve or deny the request before completing the start request.

The callback request contains a JSON body describing the verification and signed headers that
your server can authenticate:

```
POST /your/callback/path HTTP/1.1
Host: your-server.example.com
Content-Type: application/json
Authorization: Application <application-key>:<signature>
x-timestamp: 1752573720

{
  "event": "verification_request",
  "data": {
    "id": "0f9c8b7a-1e2d-4c3b-9a8f-7e6d5c4b3a21",
    "destination": "4915112345678",
    "delivery_method": "sms"
  }
}
```

The `Authorization` and
`x-timestamp` headers let your server verify the request signature. The JSON body identifies
the verification that is waiting for your server's approval or denial. The following table
describes the fields in that JSON body:

| Field | Type | Description |
| --- | --- | --- |
| `event` | `string` | Always `verification_request` for this callback. |
| `data.id` | `string` | The verification identifier (UUID). |
| `data.destination` | `string` | The number being verified, in E.164 without a leading `+`. |
| `data.delivery_method` | `string` | The requested delivery method: `sms` or `callout`. |

### Verifying the signature

Each callback includes a signature that lets your server confirm that the request came from
the Verification API and was not modified in transit. Verify this signature before using the
callback data or returning an approval decision.

The callback uses the same HMAC-SHA256 signing scheme as `application` authentication,
with your application secret as the signing key. The `Authorization` header contains
`Application <application-key>:<signature>`, and `x-timestamp` contains the signing time
in Unix seconds.

Recreate the signature from the callback request and compare it with the signature in the
`Authorization` header. See [Signed requests](authentication.html#otp-verification-signed-requests) for the
string-to-sign format and signing procedure. For
a callback, the `PATH` component is the path from your configured callback URL.

Warning

The callback endpoint is reachable from the public internet. Verify the signature and
timestamp before approving or processing the callback request.

### Callback response

After receiving the callback request, your callback endpoint must respond to the Verification
API over the same HTTP connection. This response is sent from your backend to the Verification
API, not to the application that started the verification.

Return an HTTP `2xx` response with a JSON body containing an `action` field. Use `allow`
to approve the verification request or `deny` to reject it.

| Response from your callback | Decision |
| --- | --- |
| `2xx` with `{ "action": "allow" }` | Approve the verification request. |
| `2xx` with `{ "action": "deny" }` | Reject the verification request. |
| No usable callback response | Treat the callback as unsuccessful and deny the verification request. |

The following examples show the responses your callback endpoint can return:

Allow

```
HTTP/1.1 200 OK
Content-Type: application/json

{
  "action": "allow"
}
```


Deny

```
HTTP/1.1 200 OK
Content-Type: application/json

{
  "action": "deny"
}
```

### Result returned by the Verification API

After processing the callback response, the Verification API completes the original
[start request](api-reference/start-verification.html#otp-verification-api-start) and returns the verification result to the
application that started it.

The following table shows how each callback outcome appears in the start response:

| Callback outcome | Start response | Delivery result |
| --- | --- | --- |
| `allow` | `201 Created` with `status` `pending` | The Verification API creates the verification and attempts to deliver the challenge. |
| `deny` | `201 Created` with `status` `denied` and `error_code` `denied_by_callback` | The Verification API creates the denied verification. No challenge is delivered. |
| No usable callback response | `201 Created` with `status` `denied` and `error_code` `denied_invalid_callback_response` | The Verification API creates the denied verification. No challenge is delivered. |

A callback response is unusable when the callback endpoint returns a non-`2xx` status,
times out, encounters a transport error, returns invalid JSON, provides an unsupported
`action`, or returns a response body larger than 8 KB.

Approved and denied start requests can both return `201 Created`. This status confirms that
the verification record was created. It does not confirm that the callback approved the
request or that the challenge was delivered. Inspect `status` and `error_code` to
determine the result.

Note

If `public` authentication is used without a callback URL configured on the OTP
application, no callback request is sent. The Verification API creates the verification
with `status` `denied` and `error_code` `denied_missing_callback_url`. The original
start request can still return `201 Created`.

### Timeouts

Callback requests use a **5-second connection timeout** and a **15-second read timeout**. If
your server cannot be reached or does not return a usable response within these limits, the
verification is created with `status` `denied` and `error_code`
`denied_invalid_callback_response`.

Design your callback endpoint to return its decision within these timeout limits.

On this page
