# Report a verification

Submit the code the user received by SMS or phone call. The endpoint identifies the
verification by the `id` returned when it was started and checks the submitted code against
that verification.

A correct value changes a pending verification to `verified`. An incorrect value can leave
the verification available for another attempt until the attempt limit is reached. Reporting
a verification that has already reached a terminal state does not reopen or change it.

To submit a value without storing the verification `id`, use
[Report a verification by number](report-verification-by-number.html#otp-verification-api-report-by-number).

## Request

HTTP method: `PATCH` (`PUT` is accepted as an alias)

Path: `/api/v1/verifications/{id}`

### Path parameters

| Name | Type | Description |
| --- | --- | --- |
| `id` | `string` | Verification identifier (UUID) returned by [Start a verification](start-verification.html#otp-verification-api-start). |

### Request body

The request body contains a top-level `data` object. Use `code` for both `sms` and
`callout`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `delivery_method` | `string` | Yes | Must match the method used to start the verification. Supported values are `sms` and `callout`. |
| `code` | `string` | Yes | The code the user received by SMS or phone call. |

## Response

When the API accepts the report request, it returns `200 OK` with the
[verification object](index.html#otp-verification-object-reference) under a top-level `data`
key. Authentication, lookup, and validation errors return an `errors` array.

The following table lists the HTTP status codes returned by this endpoint:

| Status | Meaning |
| --- | --- |
| `200 OK` | The report was processed. Inspect the returned `status` and `error_code` to determine the current verification state. |
| `401 Unauthorized` | Authentication failed, credentials are missing or invalid, or the authentication mode is below the minimum configured for the OTP application. |
| `404 Not Found` | No verification with the specified `id` exists for the OTP application. |
| `422 Unprocessable Content` | The submitted report could not be accepted. This includes an incorrect code or a mismatched `delivery_method`. |

A correct code returns `200 OK` with `status` `verified`. An incorrect code returns
`422 Unprocessable Content` with `code_invalid`. The verification remains available for
another report until the attempt limit is reached. After three unsuccessful reports, it
becomes `failed` with `error_code` `too_many_attempts`.

If the challenge has not yet been dispatched, the API can return `422 Unprocessable
Content` with `not_ready_to_report`. Submit the value again after the challenge has been
sent or the call has been placed.

See [Errors and status codes](errors.html#otp-verification-api-errors) for the error response
structure and available error codes.

Warning

Each report can consume one of the three allowed attempts. Do not automatically retry a
report after a timeout or another ambiguous network failure. Check the verification status
before deciding whether the user should submit the value again.

## Examples

The REST API examples use HTTP Basic authentication. See
[REST API authentication](../authentication.html#otp-verification-authentication) for credential and header
requirements.

The SDK examples assume that the corresponding SDK is installed and initialized, and that
the verification returned by the start operation is available. See the
[Ruby](../sdks/ruby.html#otp-verification-sdk-ruby),
[Python](../sdks/python.html#otp-verification-sdk-python),
[Node.js](../sdks/node.html#otp-verification-sdk-node),
[PHP](../sdks/php.html#otp-verification-sdk-php),
[iOS](../sdks/ios.html#otp-verification-sdk-ios),
[Android](../sdks/android.html#otp-verification-sdk-android),
[React Native](../sdks/react-native.html#otp-verification-sdk-react-native), and
[Dart](../sdks/dart.html#otp-verification-sdk-dart) SDK pages.

The identifiers, expiration times, fees, and codes shown in the examples are illustrative.

### REST API

Send a `PATCH` request with the verification `id` and the value reported by the user. A
correct value returns the updated verification with `status` `verified`.

SMS

http

```
PATCH /api/v1/verifications/0f9c8b7a-1e2d-4c3b-9a8f-7e6d5c4b3a21 HTTP/1.1
Host: verification.didww.com
Content-Type: application/json
Accept: application/json
Authorization: Basic eW91cl9hcHBfa2V5OnlvdXJfYXBwX3NlY3JldA==

{
  "data": {
    "delivery_method": "sms",
    "code": "123456"
  }
}
```

curl

```
curl -i -X PATCH https://verification.didww.com/api/v1/verifications/0f9c8b7a-1e2d-4c3b-9a8f-7e6d5c4b3a21 -H "Accept: application/json" -H "Content-Type: application/json" --data-raw '{"data": {"code": "123456", "delivery_method": "sms"}}' --user your_app_key:your_app_secret
```

response

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

{
  "data": {
    "id": "0f9c8b7a-1e2d-4c3b-9a8f-7e6d5c4b3a21",
    "destination": "4915112345678",
    "delivery_method": "sms",
    "fee": "0.06",
    "status": "verified",
    "error_code": null,
    "error_detail": null,
    "expires_at": "2026-07-15T10:05:00.000Z",
    "sms": {
      "template": "Your code is {{CODE}}",
      "language": "en-US",
      "interception_timeout": 300,
      "code_length": 6
    }
  }
}
```


Phone call

http

```
PATCH /api/v1/verifications/2b3c4d5e-6f70-4b3c-9d0e-1f2a3b4c5d6e HTTP/1.1
Host: verification.didww.com
Content-Type: application/json
Accept: application/json
Authorization: Basic eW91cl9hcHBfa2V5OnlvdXJfYXBwX3NlY3JldA==

{
  "data": {
    "delivery_method": "callout",
    "code": "123456"
  }
}
```

curl

```
curl -i -X PATCH https://verification.didww.com/api/v1/verifications/2b3c4d5e-6f70-4b3c-9d0e-1f2a3b4c5d6e -H "Accept: application/json" -H "Content-Type: application/json" --data-raw '{"data": {"code": "123456", "delivery_method": "callout"}}' --user your_app_key:your_app_secret
```

response

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

{
  "data": {
    "id": "2b3c4d5e-6f70-4b3c-9d0e-1f2a3b4c5d6e",
    "destination": "4915112345678",
    "delivery_method": "callout",
    "fee": "0.08",
    "status": "verified",
    "error_code": null,
    "error_detail": null,
    "expires_at": "2026-07-15T10:05:00.000Z",
    "callout": {
      "language": "de-DE",
      "code_length": 6
    }
  }
}
```

### Ruby SDK

The Ruby SDK sends a `PATCH` request when `report_verification(...)` is called and returns
the updated verification object.

SMS

```
result = client.report_verification(
  verification.id,
  delivery_method: "sms",
  code:            "123456"
)

result.status    # => "verified"
result.verified? # => true
```


Phone call

```
result = client.report_verification(
  verification.id,
  delivery_method: "callout",
  code:            "123456"
)

result.status    # => "verified"
result.verified? # => true
```

### Python SDK

The Python SDK sends a `PATCH` request when `report_verification(...)` is called and
returns the updated `Verification`.

SMS

```
result = client.report_verification(
    verification.id,
    delivery_method="sms",
    code="123456",
)

result.status  # "verified"
```


Phone call

```
result = client.report_verification(
    verification.id,
    delivery_method="callout",
    code="123456",
)

result.status  # "verified"
```

An incorrect code raises `DidwwValidationError` with `code_invalid`, as described above:
the verification stays `pending` and can be reported again. Once three attempts are used,
the next report returns a `Verification` whose `status` is `failed`. A report is never
retried by the SDK: each one consumes an attempt.

### Node.js SDK

The Node.js SDK sends a `PATCH` request when `reportVerification(...)` is called and
returns the updated verification.

SMS

```
const result = await client.reportVerification(verification.id, {
  deliveryMethod: 'sms',
  code: '123456',
});

result.status; // 'verified'
```


Phone call

```
const result = await client.reportVerification(verification.id, {
  deliveryMethod: 'callout',
  code: '123456',
});

result.status; // 'verified'
```

If a report times out after it may have reached the API, call `getVerification` rather than
sending it again.

### PHP SDK

The PHP SDK sends a `PATCH` request when `reportVerification(...)` is called and returns
the updated `Verification`.

SMS

```
$result = $client->reportVerification($verification->id, DeliveryMethod::Sms, '123456');

$result->status; // "verified"
```


Phone call

```
$result = $client->reportVerification($verification->id, DeliveryMethod::Callout, '123456');

$result->status; // "verified"
```

An incorrect code throws `ValidationException` with `code_invalid`, as described above:
the verification stays `pending` and can be reported again. Once three attempts are used,
the next report returns a `Verification` whose `status` is `failed`. A report is never
retried by the SDK: each one consumes an attempt.

### iOS SDK

The iOS SDK sends a `PUT` request when `verify(...)` is called and returns a
`VerificationResult` containing the updated state. It selects the delivery method from the
`Verification` returned by `start(...)` and rejects a code that does not match that method
before sending a request.

SMS

```
let result = try await client.verify(
    verification,
    code: "123456"
)

result.status // .verified
```


Phone call

```
let result = try await client.verify(
    verification,
    code: "123456"
)

result.status // .verified
```

### Android SDK

The Android SDK reports through the `VerificationHandle` returned by `start(...)`. The
following examples assume that `handle.states` is already being collected exactly once.

Call `submit(...)` with the code. The handle reports it for the selected delivery method.

SMS

```
handle.submit("123456")
```


Phone call

```
handle.submit("123456")
```

The existing `states` collection emits `VerificationState.Submitting` while the report is
in progress. A correct value then emits `VerificationState.Verified`. If the API rejects the
value but allows another attempt, the flow returns to `VerificationState.AwaitingInput` with
`lastError` set. A terminal outcome emits `VerificationState.Failed` or
`VerificationState.Expired`.

### Dart SDK

The Dart SDK sends a `PATCH` request when `reportVerification(...)` is called. The
reported value is wrapped in a `ReportValue`, and the SDK throws
`ChannelMismatchException` **before sending anything** when the value does not suit the
delivery method.

SMS

```
final finished = await client.reportVerification(
  started.id,
  deliveryMethod: DeliveryMethod.sms,
  value: const ReportValue.code('123456'),
);

finished.knownStatus; // VerificationStatus.verified
```


Phone call

```
final finished = await client.reportVerification(
  started.id,
  deliveryMethod: DeliveryMethod.callout,
  value: const ReportValue.code('123456'),
);

finished.knownStatus; // VerificationStatus.verified
```

With `VerificationSession`, call `session.submit(code)` instead and read the result from
the state stream.

### React Native SDK

The React Native SDK reports through the `useVerification()` controller created when the
verification was started. The controller selects the delivery method from the active
verification, so the call is the same for both channels:

```
controller.submit('123456');
```

`state.kind` moves to `'submitting'`, then to `'verified'`. If the API rejects the code
but allows another attempt, it returns to `'awaitingInput'` with the error attached. On
Android a code captured from the SMS is handed to you as `state.kind === 'captured'`; it is
not submitted for you.

On this page
