# Start a verification (/docs/api-reference/verifications/start-verification)

Sends a one-time code to a phone number (SMS) or an email address and returns the verification, `pending` until you check the code the end user typed (`POST /v1/verifications/check`). Starting again for the same destination while it is pending sends a new code (the earlier one stops working), up to 5 times. Before a new verification costs anything, the fraud guard checks it (your Risk lists, premium-rate numbers, Tor, and velocity per IP, user, number range and country, with how often their codes are entered): a blocked one is stored with its `reasons` and nothing is sent. Pass the end user's `ip` (and your `user_id`) so it can tell them apart. SMS codes come from coss.com's shared numbers unless the environment's Verify settings name one of your own; email codes from your sender, else coss.com's. Sandbox (`coss_test_…`) keys deliver nothing and the code is always zeros.

`POST https://api.coss.com/v1/verifications`

Authenticate with your API key as a bearer token (`Authorization: Bearer coss_test_…`). Responses are JSON with snake_case fields; errors are `{ error: { type, code, message, param?, issues?, request_id } }` (https://coss.com/docs/api-reference/errors.md).

## Headers

- `Idempotency-Key` (string, 1-256 characters): Makes retries safe: a repeated request with the same key and body returns the original verification instead of sending another code, for 24 hours. 1-256 characters.

## Request body

- `to` (string, required, 1-320 characters): Where the code goes: a mobile number in E.164 form, or an email address. A pending verification to the same destination and channel is reused: its code is sent again.
- `channel` ("sms" | "email"): `sms` or `email`. Default: `sms` for a phone number, `email` for an address.
- `ip` (string, at most 64 characters): The end user's IP address. The fraud guard uses it (velocity, Tor); recommended.
- `user_id` (string, 1-256 characters): Your id for the end user, for the fraud guard's velocity and your Risk lists.
- `tags` (object[], at most 10 items): Up to 10 tags.
  - `name` (string, required)
  - `value` (string, required)

## Example request

TypeScript (the `coss` package):

```ts
import { Coss } from "coss";

const coss = new Coss({
  apiKey: process.env.COSS_API_KEY,
  baseUrl: "https://api.coss.com",
});

const { data, error } = await coss.verify.start({
  to: "+14155550199",
  ip: "203.0.113.7",
  user_id: "user_42",
}, {
  idempotencyKey: "welcome-user-123",
});
```

HTTP:

```sh
curl -X POST "https://api.coss.com/v1/verifications" \
  -H "Authorization: Bearer $COSS_API_KEY" \
  -H "Idempotency-Key: welcome-user-123" \
  -H "Content-Type: application/json" \
  -d '{
  "to": "+14155550199",
  "ip": "203.0.113.7",
  "user_id": "user_42"
}'
```

## Response 201

The verification, `pending`, with the send that just went out.

- `object` ("verification", required)
- `id` (string, required)
- `channel` ("sms" | "email", required)
- `to` (string, required)
- `country` (string | null, required): ISO 3166 country of a phone number.
- `status` ("pending" | "approved" | "expired" | "canceled" | "failed" | "blocked", required): `pending` until the right code is checked (`approved`) or it ends: `expired`, `canceled`, `failed` (too many wrong codes, or the code couldn't be sent) or `blocked` (the fraud guard stopped it before anything was sent).
- `failure` (object | null, required): Why it `failed`.
  - `code` (string, required)
  - `message` (string, required)
- `reasons` (object[], required): Why it was `blocked` (empty otherwise).
  - `code` (string, required)
  - `value` (number)
- `attempts` (integer, required): Wrong codes checked so far.
- `max_attempts` (integer, required)
- `sends` (object[], required): Each time the code was sent.
  - `channel` ("sms" | "email", required)
  - `route` ("own_number" | "shared_number" | "email" | "coss_email", required): `own_number`: one of your numbers; `shared_number`: coss.com's; `email`: your sender; `coss_email`: coss.com's sender.
  - `sms_id` (string | null, required): The SMS, in your environment: `own_number`, or any in a sandbox environment (`null` for coss.com's shared numbers' messages).
  - `email_id` (string | null, required): The email (`email` route only).
  - `created_at` (string, required, date-time)
- `tags` (object[], required)
  - `name` (string, required)
  - `value` (string, required)
- `sandbox` (boolean, required): Made in a sandbox environment: simulated, and the code is always zeros.
- `expires_at` (string, required, date-time)
- `approved_at` (string | null, required, date-time)
- `created_at` (string, required, date-time)

Example:

```json
{
  "object": "verification",
  "id": "vrf_01k8e2m4q2f3e9v6t5r1n0p8yz",
  "channel": "sms",
  "to": "+14155550199",
  "country": "US",
  "status": "pending",
  "failure": null,
  "reasons": [],
  "attempts": 0,
  "max_attempts": 5,
  "sends": [
    {
      "channel": "sms",
      "route": "shared_number",
      "sms_id": "sms_01k8e2m4q2f3e9v6t5r1n0p8yz",
      "email_id": null,
      "created_at": "2026-11-10T12:00:00.000Z"
    }
  ],
  "tags": [],
  "sandbox": false,
  "expires_at": "2026-11-10T12:10:00.000Z",
  "approved_at": null,
  "created_at": "2026-11-10T12:00:00.000Z"
}
```

## Errors

- `400`: The body is not valid JSON (`invalid_json`) or the `Idempotency-Key` header is invalid (`invalid_idempotency_key`). Also when the `Coss-Version` header names an unknown API version (`invalid_api_version`).
- `401`: The API key is missing, invalid, revoked or expired (`authentication_error`).
- `402`: Live sending is paused for an overdue invoice or the spend limit (`billing_hold`) or a used-up budget (`budget_exceeded`), or the plan doesn't include Verify or the destination country isn't priced (`plan_feature_unavailable`) (`billing_error`). Never retried: it needs a plan change or a payment.
- `403`: The API key lacks the permission this endpoint needs (`permission_error`), or its organization is suspended (`organization_suspended`).
- `409`: Requests with this `Idempotency-Key` are racing (`request_in_progress`).
- `422`: The body failed validation (`validation_error`, see `issues`); `to` isn't a valid number or address (`invalid_destination`) or its country isn't allowed (`country_not_allowed`); the fraud guard blocked it (`verification_blocked`: the verification is stored with its `reasons`); the code was already sent 5 times (`too_many_sends`); the recipient opted out or is suppressed (`recipient_suppressed`); the code couldn't be sent (`send_failed`, `sender_not_ready`…); or the `Idempotency-Key` was already used with a different body (`idempotency_key_reused`).
- `429`: Too many requests with this API key (`rate_limited`), or too many codes to this destination or from coss.com's shared numbers today (`quota_exceeded`). Retry after `Retry-After` seconds.
- `500`: Unexpected server error (`api_error` / `internal_error`).
- `503`: coss.com's shared numbers aren't available for this destination (`shared_sender_unavailable`): name your own number in Verify's settings.