> For the complete documentation index, see [llms.txt](https://help.delpha.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.delpha.io/delpha-api-and-mcp/api/phone.md).

# Phone

The phone operations normalize a submitted number to E.164 and report its line type, carrier, country and timezone. The number is normalized, then verified to confirm that the line exists and to classify it — never to identify whose number it is.

Three dimensions are scored:

* **Completeness:** whether a phone number is present and structurally complete.
* **Validity:** whether the number follows the national or international format and is neither blacklisted nor clearly invalid.
* **Consistency:** the original input compared with its normalized international form.

A `country` or `country_code` supplies the numbering plan for a national-format number; a number in E.164 form carries its own. These operations validate and normalize only — they do not search for a number.

## Validate and normalize a phone number

> Synchronous phone validation: normalize a phone number and receive its type, carrier, timezone, zone, area and validity in a single request (no job\_id polling). Provide the number with an international (+) prefix, or pass \`country\` (a name or ISO code) for national-format numbers.\
> \
> This result is obtained by parsing the submitted number and verifying it against data from carriers and cross-carrier network enablers, through Delpha's providers. Delpha verifies only the number itself (validity, line type, carrier, connection status) and does not link it to an individual, a name or any other personal data. Those providers receive the number in E.164 form and a two-letter country code, and nothing else.\
> \
> A live-mode call reserves credits; unless your team has set resolution retention to 0, the call and its result are also recorded in your team's resolution history for the window your team configured (up to 30 days on the free tier, 180 on a paid plan). Repeating the same request is a new call and a new charge. Test-mode calls are answered from the published fixture table and reserve nothing — see the API documentation's \*\*Test mode\*\* section.

```json
{"openapi":"3.1.0","info":{"title":"Delpha Data Quality API","version":"2.3.2"},"tags":[{"name":"Phone","description":"The phone operations normalize a submitted number to E.164 and report its line type, carrier, country and timezone. The number is normalized, then verified to confirm that the line exists and to classify it — never to identify whose number it is.\n\nThree dimensions are scored:\n- **Completeness:** whether a phone number is present and structurally complete.\n- **Validity:** whether the number follows the national or international format and is neither blacklisted nor clearly invalid.\n- **Consistency:** the original input compared with its normalized international form.\n\nA `country` or `country_code` supplies the numbering plan for a national-format number; a number in E.164 form carries its own. These operations validate and normalize only — they do not search for a number."}],"servers":[{"url":"https://api.delpha.io","description":"Production API server"}],"security":[{"oauth2":["api/access"]},{"apiKey":[]}],"components":{"securitySchemes":{"oauth2":{"type":"oauth2","description":"OAuth 2.0 client-credentials flow (machine-to-machine). Token URL: https://secure.delpha.io/oauth2/token. The `api/access` scope is all-access: it grants every operation this document publishes. Per-use-case scoping, per-key credit ceilings and expiry are properties of an API key, not of this flow — use an API key when a credential must be restricted.","flows":{"clientCredentials":{"tokenUrl":"https://secure.delpha.io/oauth2/token","scopes":{"api/access":"Access to the public API"}}}},"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"dlp_","description":"Native Delpha API key. Send it as an HTTP bearer token (`Authorization: Bearer dlp_...`). Keys are issued from the Delpha app (Developer → API keys) and are billed per team against the team's credit balance."}},"schemas":{"PhoneSyncOutput":{"additionalProperties":true,"description":"Public response for the synchronous phone validation.","properties":{"code":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Stable machine-readable status/error code (see ErrorCode).","title":"Code"},"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The API Gateway request id, for support correlation.","title":"Request Id"},"doc_url":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Link to error documentation (present when published).","title":"Doc Url"},"message":{"description":"The message to return","title":"Message","type":"string"},"status":{"description":"The status of the process","enum":["SUCCESS","ERROR"],"title":"Status","type":"string"},"status_code":{"description":"The status code to return","title":"Status Code","type":"integer"},"result":{"anyOf":[{"$ref":"#/components/schemas/PhoneSyncResult"},{"type":"null"}],"default":null,"description":"The phone assessment result. Present when status == 'SUCCESS'."},"process_time":{"anyOf":[{"type":"number"},{"type":"null"}],"default":null,"description":"The processing time of the request, in seconds.","title":"Process Time"}},"required":["message","status","status_code"],"title":"PhoneSyncOutput","type":"object"},"PhoneSyncResult":{"additionalProperties":true,"description":"The trimmed result payload for a successful synchronous phone assessment.","properties":{"normalized_value":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The normalized E.164 phone value.","title":"Normalized Value"},"phone_type":{"anyOf":[{"$ref":"#/components/schemas/PhoneType"},{"type":"null"}],"default":null,"description":"The type of phone number."},"carrier":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The carrier of the phone number.","title":"Carrier"},"timezone":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The timezone of the phone number.","title":"Timezone"},"zone":{"anyOf":[{"$ref":"#/components/schemas/Zone"},{"type":"null"}],"default":null,"description":"The country/zone (ISO code) of the phone number."},"area":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The geographic area of the phone number.","title":"Area"},"valid":{"description":"Whether the phone number is valid (carrier-confirmed where available).","title":"Valid","type":"boolean"},"activity_score":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"description":"How recently the line has shown activity, on the second-tier line-type provider's own 0-100 scale. `null` whenever that provider was not consulted — which is the common case, since it is called only to resolve an ambiguous line type.","title":"Activity Score"},"is_prepaid":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":null,"description":"Whether the number is a prepaid line, from the second-tier line-type provider. `null` whenever that provider was not consulted — see `activity_score`.","title":"Is Prepaid"}},"required":["valid"],"title":"PhoneSyncResult","type":"object"},"PhoneType":{"description":"Enum representing the possible phone number types.","enum":["FIXED_LINE","MOBILE","FIXED_LINE_OR_MOBILE","TOLL_FREE","PREMIUM_RATE","SHARED_COST","VOIP","PERSONAL_NUMBER","PAGER","UAN","VOICEMAIL"],"title":"PhoneType","type":"string"},"Zone":{"description":"Enum representing the possible country/zone codes for phone numbers\n(ISO 3166-1 alpha-2).","enum":["AC","AD","AE","AF","AG","AI","AL","AM","AO","AR","AS","AT","AU","AW","AX","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ","BL","BM","BN","BO","BQ","BR","BS","BT","BW","BY","BZ","CA","CC","CD","CF","CG","CH","CI","CK","CL","CM","CN","CO","CR","CU","CV","CW","CX","CY","CZ","DE","DJ","DK","DM","DO","DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ","FK","FM","FO","FR","GA","GB","GD","GE","GF","GG","GH","GI","GL","GM","GN","GP","GQ","GR","GT","GU","GW","GY","HK","HN","HR","HT","HU","ID","IE","IL","IM","IN","IO","IQ","IR","IS","IT","JE","JM","JO","JP","KE","KG","KH","KI","KM","KN","KP","KR","KW","KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT","LU","LV","LY","MA","MC","MD","ME","MF","MG","MH","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI","NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF","PG","PH","PK","PL","PM","PR","PS","PT","PW","PY","QA","RE","RO","RS","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO","SR","SS","ST","SV","SX","SY","SZ","TA","TC","TD","TG","TH","TJ","TK","TL","TM","TN","TO","TR","TT","TV","TW","TZ","UA","UG","US","UY","UZ","VA","VC","VE","VG","VI","VN","VU","WF","WS","XK","YE","YT","ZA","ZM","ZW"],"title":"Zone","type":"string"},"Error":{"type":"object","title":"Error","description":"Typed error envelope emitted by the API for handler-level 4xx/5xx responses. Extends the success envelope with a stable machine-readable `code` and a correlation `request_id`.","properties":{"status":{"type":"string","enum":["ERROR"],"description":"Always \"ERROR\" for an error response."},"status_code":{"type":"integer","description":"The HTTP status code of the response."},"message":{"type":"string","description":"Human-readable, PII-free error message."},"code":{"type":"string","enum":["validation_error","unauthorized","payment_required","not_found","conflict","rate_limited","backend_error","backend_timeout","service_unavailable","job_timeout","internal_error","unsupported_registry","idempotency_key_reuse","idempotency_in_progress","idempotency_key_invalid"],"description":"Stable machine-readable error code. `ok` and `job_running` are the success-side codes and never appear in an error body."},"request_id":{"type":"string","description":"API Gateway request id, for support correlation. The same value is returned in the `x-amzn-RequestId` response header on every response, including the 401/403/429 API Gateway generates itself."},"doc_url":{"type":"string","description":"Link to error documentation. Present when error documentation is published."}},"required":["status","status_code","message"],"additionalProperties":true},"GatewayError":{"type":"object","title":"GatewayError","description":"Error body returned by API Gateway itself, before a handler runs: 401 (no or invalid credential), 403 (the API key's scopes do not cover this operation, or the path is not routed here) and 429 (stage throttle; carries `Retry-After`). It has no `code` and no `request_id`; correlate it through the `x-amzn-RequestId` response header instead. A 401 a handler emits after the gateway admitted the request carries the full `Error` envelope.","properties":{"status":{"type":"string","enum":["ERROR"],"description":"Always \"ERROR\"."},"status_code":{"type":"integer","description":"The HTTP status code of the response (401, 403 or 429)."},"message":{"type":"string","description":"Human-readable error message."}},"required":["message"],"additionalProperties":true}},"headers":{"RateLimitLimit":{"description":"Your request allowance for this operation in the current 60-second window. It is counted per credential owner AND per operation: spending it on one operation does not reduce what you may send to another.","schema":{"type":"string"}},"RateLimitRemaining":{"description":"Requests still available to you on this operation in the current window. `0` on the request that exhausts it and on the refusals that follow.","schema":{"type":"string"}},"RateLimitReset":{"description":"When the current window resets, as **UTC epoch seconds** — an absolute instant, not a number of seconds to wait. Use `Retry-After` for the delay.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of the allowance, as a structured field of `draft-ietf-httpapi-ratelimit-headers` (an Internet-Draft, not a published standard): `\"<policy>\";r=<remaining>;t=<seconds to reset>`. Exactly one policy governs a request, so the list always carries one member.","schema":{"type":"string"}},"RateLimitPolicy":{"description":"The allowance that governed this request, as a structured field of `draft-ietf-httpapi-ratelimit-headers` (an Internet-Draft, not a published standard): `\"<policy>\";q=<quota>;w=<window seconds>`. `submit` governs submit and synchronous operations, `poll` the poll operations; the policy for each operation is also published in its `x-rate-limit`.","schema":{"type":"string"}},"DelphaMode":{"description":"Present on every response the API renders to a **test-mode** key, and on no other: absence means the call was live. A test-mode call is answered from the published fixture table, reserves no credits and reaches no verification backend. The 401, 403 and shared-floor 429 the gateway answers before the API sees the request carry no per-request headers, so they never carry this one. See the *Test mode* section of this document.","schema":{"type":"string","enum":["sandbox"]}}}},"paths":{"/v1/phone":{"get":{"tags":["Phone"],"summary":"Validate and normalize a phone number","description":"Synchronous phone validation: normalize a phone number and receive its type, carrier, timezone, zone, area and validity in a single request (no job_id polling). Provide the number with an international (+) prefix, or pass `country` (a name or ISO code) for national-format numbers.\n\nThis result is obtained by parsing the submitted number and verifying it against data from carriers and cross-carrier network enablers, through Delpha's providers. Delpha verifies only the number itself (validity, line type, carrier, connection status) and does not link it to an individual, a name or any other personal data. Those providers receive the number in E.164 form and a two-letter country code, and nothing else.\n\nA live-mode call reserves credits; unless your team has set resolution retention to 0, the call and its result are also recorded in your team's resolution history for the window your team configured (up to 30 days on the free tier, 180 on a paid plan). Repeating the same request is a new call and a new charge. Test-mode calls are answered from the published fixture table and reserve nothing — see the API documentation's **Test mode** section.","operationId":"validatePhone","responses":{"200":{"description":"Job succeeded — the result payload is populated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneSyncOutput"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"},"X-Delpha-Job-Id":{"description":"The reservation's job id for this call (present once admission succeeded, including on post-admission errors). The same call is addressed by this id in the Delpha AI console's history (Logs / Resolutions).","schema":{"type":"string"}}}},"400":{"description":"Validation error — the request body or query parameters were rejected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"401":{"description":"Authentication failed — invalid or missing OAuth 2.0 token or API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayError"}}},"headers":{}},"402":{"description":"Payment required — the team has insufficient credits.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"403":{"description":"Forbidden — the credential is valid but not allowed here: the API key's scopes do not cover this operation, or the operation is not routed in this environment (API Gateway then answers `Missing Authentication Token`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayError"}}},"headers":{}},"409":{"description":"Conflict — the credit reservation expired before delivery; resubmit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"},"X-Delpha-Job-Id":{"description":"The reservation's job id for this call (present once admission succeeded, including on post-admission errors). The same call is addressed by this id in the Delpha AI console's history (Logs / Resolutions).","schema":{"type":"string"}}}},"429":{"description":"Too many requests — one of two limits was exceeded. Your allowance for this operation in the current 60-second window: an `Error` body with `code: rate_limited`, `Retry-After` set to the seconds left in the window, and the quota headers with `X-RateLimit-Remaining: 0`; nothing is charged. Or the per-operation floor every caller of that operation shares: a `GatewayError` body with `Retry-After: 1` and no quota headers, refused before the API sees the request.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/GatewayError"}]}}},"headers":{"Retry-After":{"description":"Seconds to wait before retrying: the time left in your current window on the allowance refusal, and `1` on the shared per-operation floor.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"RateLimit":{"$ref":"#/components/headers/RateLimit","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no per-request headers."}}},"502":{"description":"Bad gateway — the upstream service returned an error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"},"X-Delpha-Job-Id":{"description":"The reservation's job id for this call (present once admission succeeded, including on post-admission errors). The same call is addressed by this id in the Delpha AI console's history (Logs / Resolutions).","schema":{"type":"string"}}}},"503":{"description":"Service unavailable — a downstream dependency was unreachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"},"X-Delpha-Job-Id":{"description":"The reservation's job id for this call (present once admission succeeded, including on post-admission errors). The same call is addressed by this id in the Delpha AI console's history (Logs / Resolutions).","schema":{"type":"string"}}}},"504":{"description":"Gateway timeout — the job exceeded its processing ceiling.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"},"X-Delpha-Job-Id":{"description":"The reservation's job id for this call (present once admission succeeded, including on post-admission errors). The same call is addressed by this id in the Delpha AI console's history (Logs / Resolutions).","schema":{"type":"string"}}}}},"parameters":[{"name":"phone","in":"query","required":true,"description":"The phone number. Include the international code (e.g. `+33672557378`) for automatic country detection, otherwise provide `country`.","schema":{"type":"string"}},{"name":"country","in":"query","required":false,"description":"The country of the phone number — a country name (`France`) OR a 2-letter ISO code (`FR`). Required when the phone has no international (+) prefix.","schema":{"type":"string"}}]}}}}
```

## Find & validate phone number

> Submit an asynchronous job to validate and normalize a phone number (line type, carrier, reachability, country detection): send \`phone\`, with \`country\_code\` when it is not in E.164 form. Returns a \`job\_id\` — pass it to \`getPhoneResult\` to retrieve the E.164-formatted number, the resolved country, and validity / reachability scores.\
> \
> \`phone\` is required. A number in E.164 form (e.g. \`+33672557378\`) carries its own country. A national-format number (e.g. \`0672557378\`) is normalized only when \`country\_code\` (ISO 3166-1 alpha-2) is supplied; a call with neither is rejected with 400.\
> \
> This result is obtained by parsing the submitted number and verifying it against data from carriers and cross-carrier network enablers, through Delpha's providers. Delpha verifies only the number itself (validity, line type, carrier, connection status) and does not link it to an individual, a name or any other personal data. Those providers receive the number in E.164 form and a two-letter country code, and nothing else.\
> \
> A live-mode call reserves credits and creates a job record retained for 10 days; unless your team has set resolution retention to 0, the call and its result are also recorded in your team's resolution history for the window your team configured (up to 30 days on the free tier, 180 on a paid plan). Re-submitting the same input starts a new job and a new charge, unless the request carries an \`Idempotency-Key\` already used with the same body within the last 24 hours — that returns the original \`job\_id\` and reserves nothing. Test-mode calls are answered from the published fixture table and reserve nothing — see the API documentation's \*\*Test mode\*\* section.

```json
{"openapi":"3.1.0","info":{"title":"Delpha Data Quality API","version":"2.3.2"},"tags":[{"name":"Phone","description":"The phone operations normalize a submitted number to E.164 and report its line type, carrier, country and timezone. The number is normalized, then verified to confirm that the line exists and to classify it — never to identify whose number it is.\n\nThree dimensions are scored:\n- **Completeness:** whether a phone number is present and structurally complete.\n- **Validity:** whether the number follows the national or international format and is neither blacklisted nor clearly invalid.\n- **Consistency:** the original input compared with its normalized international form.\n\nA `country` or `country_code` supplies the numbering plan for a national-format number; a number in E.164 form carries its own. These operations validate and normalize only — they do not search for a number."}],"servers":[{"url":"https://api.delpha.io","description":"Production API server"}],"security":[{"oauth2":["api/access"]},{"apiKey":[]}],"components":{"securitySchemes":{"oauth2":{"type":"oauth2","description":"OAuth 2.0 client-credentials flow (machine-to-machine). Token URL: https://secure.delpha.io/oauth2/token. The `api/access` scope is all-access: it grants every operation this document publishes. Per-use-case scoping, per-key credit ceilings and expiry are properties of an API key, not of this flow — use an API key when a credential must be restricted.","flows":{"clientCredentials":{"tokenUrl":"https://secure.delpha.io/oauth2/token","scopes":{"api/access":"Access to the public API"}}}},"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"dlp_","description":"Native Delpha API key. Send it as an HTTP bearer token (`Authorization: Bearer dlp_...`). Keys are issued from the Delpha app (Developer → API keys) and are billed per team against the team's credit balance."}},"schemas":{"PhoneSubmitOutput":{"additionalProperties":true,"description":"Response after submitting a new phone quality job.","properties":{"code":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Stable machine-readable status/error code (see ErrorCode).","title":"Code"},"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The API Gateway request id, for support correlation.","title":"Request Id"},"doc_url":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Link to error documentation (present when published).","title":"Doc Url"},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The job ID. Required if status <= 300.","title":"Job Id"},"message":{"description":"The message to return","title":"Message","type":"string"},"status":{"description":"The status of the process","enum":["SUCCESS","ERROR"],"title":"Status","type":"string"},"status_code":{"description":"The status code to return","title":"Status Code","type":"integer"}},"required":["message","status","status_code"],"title":"PhoneSubmitOutput","type":"object"},"Error":{"type":"object","title":"Error","description":"Typed error envelope emitted by the API for handler-level 4xx/5xx responses. Extends the success envelope with a stable machine-readable `code` and a correlation `request_id`.","properties":{"status":{"type":"string","enum":["ERROR"],"description":"Always \"ERROR\" for an error response."},"status_code":{"type":"integer","description":"The HTTP status code of the response."},"message":{"type":"string","description":"Human-readable, PII-free error message."},"code":{"type":"string","enum":["validation_error","unauthorized","payment_required","not_found","conflict","rate_limited","backend_error","backend_timeout","service_unavailable","job_timeout","internal_error","unsupported_registry","idempotency_key_reuse","idempotency_in_progress","idempotency_key_invalid"],"description":"Stable machine-readable error code. `ok` and `job_running` are the success-side codes and never appear in an error body."},"request_id":{"type":"string","description":"API Gateway request id, for support correlation. The same value is returned in the `x-amzn-RequestId` response header on every response, including the 401/403/429 API Gateway generates itself."},"doc_url":{"type":"string","description":"Link to error documentation. Present when error documentation is published."}},"required":["status","status_code","message"],"additionalProperties":true},"GatewayError":{"type":"object","title":"GatewayError","description":"Error body returned by API Gateway itself, before a handler runs: 401 (no or invalid credential), 403 (the API key's scopes do not cover this operation, or the path is not routed here) and 429 (stage throttle; carries `Retry-After`). It has no `code` and no `request_id`; correlate it through the `x-amzn-RequestId` response header instead. A 401 a handler emits after the gateway admitted the request carries the full `Error` envelope.","properties":{"status":{"type":"string","enum":["ERROR"],"description":"Always \"ERROR\"."},"status_code":{"type":"integer","description":"The HTTP status code of the response (401, 403 or 429)."},"message":{"type":"string","description":"Human-readable error message."}},"required":["message"],"additionalProperties":true},"PhoneSubmitInput":{"description":"Payload to submit a new phone quality job.\n\n**Important**: For proper normalization and country detection, either:\n- Provide a phone number with international code (e.g., \"+33672557378\"), OR\n- Provide the phone number along with `country` or `country_code`\n\nIf none of these conditions are met, the system may not be able to properly\ndetect the country and apply the related normalization.","properties":{"phone":{"description":"Phone number to validate. E.164 form (e.g. `+33672557378`) is preferred and self-locating; national form (e.g. `0672557378`) requires `country` or `country_code`. Required.","title":"Phone","type":"string"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Country display name in English (e.g. `France`). Supplies the numbering plan when `phone` is not in E.164 form; either this or `country_code` does, and `country_code` wins when both are set.","title":"Country"},"country_code":{"anyOf":[{"type":"string","pattern":"^[A-Z]{2}$"},{"type":"null"}],"default":null,"description":"ISO 3166-1 alpha-2 country code (e.g. `FR`, `US`, `GB`). Preferred over `country` when known. Required when `phone` is not in E.164 form.","title":"Country Code"}},"required":["phone"],"title":"PhoneSubmitInput","type":"object"}},"headers":{"RateLimitLimit":{"description":"Your request allowance for this operation in the current 60-second window. It is counted per credential owner AND per operation: spending it on one operation does not reduce what you may send to another.","schema":{"type":"string"}},"RateLimitRemaining":{"description":"Requests still available to you on this operation in the current window. `0` on the request that exhausts it and on the refusals that follow.","schema":{"type":"string"}},"RateLimitReset":{"description":"When the current window resets, as **UTC epoch seconds** — an absolute instant, not a number of seconds to wait. Use `Retry-After` for the delay.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of the allowance, as a structured field of `draft-ietf-httpapi-ratelimit-headers` (an Internet-Draft, not a published standard): `\"<policy>\";r=<remaining>;t=<seconds to reset>`. Exactly one policy governs a request, so the list always carries one member.","schema":{"type":"string"}},"RateLimitPolicy":{"description":"The allowance that governed this request, as a structured field of `draft-ietf-httpapi-ratelimit-headers` (an Internet-Draft, not a published standard): `\"<policy>\";q=<quota>;w=<window seconds>`. `submit` governs submit and synchronous operations, `poll` the poll operations; the policy for each operation is also published in its `x-rate-limit`.","schema":{"type":"string"}},"DelphaMode":{"description":"Present on every response the API renders to a **test-mode** key, and on no other: absence means the call was live. A test-mode call is answered from the published fixture table, reserves no credits and reaches no verification backend. The 401, 403 and shared-floor 429 the gateway answers before the API sees the request carry no per-request headers, so they never carry this one. See the *Test mode* section of this document.","schema":{"type":"string","enum":["sandbox"]}}}},"paths":{"/v1/phone":{"post":{"tags":["Phone"],"summary":"Find & validate phone number","description":"Submit an asynchronous job to validate and normalize a phone number (line type, carrier, reachability, country detection): send `phone`, with `country_code` when it is not in E.164 form. Returns a `job_id` — pass it to `getPhoneResult` to retrieve the E.164-formatted number, the resolved country, and validity / reachability scores.\n\n`phone` is required. A number in E.164 form (e.g. `+33672557378`) carries its own country. A national-format number (e.g. `0672557378`) is normalized only when `country_code` (ISO 3166-1 alpha-2) is supplied; a call with neither is rejected with 400.\n\nThis result is obtained by parsing the submitted number and verifying it against data from carriers and cross-carrier network enablers, through Delpha's providers. Delpha verifies only the number itself (validity, line type, carrier, connection status) and does not link it to an individual, a name or any other personal data. Those providers receive the number in E.164 form and a two-letter country code, and nothing else.\n\nA live-mode call reserves credits and creates a job record retained for 10 days; unless your team has set resolution retention to 0, the call and its result are also recorded in your team's resolution history for the window your team configured (up to 30 days on the free tier, 180 on a paid plan). Re-submitting the same input starts a new job and a new charge, unless the request carries an `Idempotency-Key` already used with the same body within the last 24 hours — that returns the original `job_id` and reserves nothing. Test-mode calls are answered from the published fixture table and reserve nothing — see the API documentation's **Test mode** section.","operationId":"findAndValidatePhone","responses":{"202":{"description":"Accepted / still running — poll the result route with the returned job_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneSubmitOutput"}}},"headers":{"Location":{"description":"Poll URL for this job (present when the response carries a job_id).","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait before polling again.","schema":{"type":"integer"}},"Idempotent-Replayed":{"description":"Set to 'true' when this 202 replays a previously accepted request with the same Idempotency-Key (no new job, no additional charge).","schema":{"type":"string"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"400":{"description":"Validation error — the request body or query parameters were rejected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"401":{"description":"Authentication failed — invalid or missing OAuth 2.0 token or API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayError"}}},"headers":{}},"402":{"description":"Payment required — the team has insufficient credits.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"403":{"description":"Forbidden — the credential is valid but not allowed here: the API key's scopes do not cover this operation, or the operation is not routed in this environment (API Gateway then answers `Missing Authentication Token`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayError"}}},"headers":{}},"409":{"description":"Conflict — the `Idempotency-Key` was already used with a different request body (`idempotency_key_reuse`: a new key admits the new body, the original body replays the original job), or the original request with this key is still being processed (`idempotency_in_progress`: the same request answers with the original job once that one is admitted).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"429":{"description":"Too many requests — one of two limits was exceeded. Your allowance for this operation in the current 60-second window: an `Error` body with `code: rate_limited`, `Retry-After` set to the seconds left in the window, and the quota headers with `X-RateLimit-Remaining: 0`; nothing is charged. Or the per-operation floor every caller of that operation shares: a `GatewayError` body with `Retry-After: 1` and no quota headers, refused before the API sees the request.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/GatewayError"}]}}},"headers":{"Retry-After":{"description":"Seconds to wait before retrying: the time left in your current window on the allowance refusal, and `1` on the shared per-operation floor.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"RateLimit":{"$ref":"#/components/headers/RateLimit","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no per-request headers."}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"502":{"description":"Bad gateway — the upstream service returned an error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneSubmitInput"}}}}}}}}
```

## Get phone validation result

> Fetch the result of a previously submitted \`findAndValidatePhone\` job by \`job\_id\`. On success, the \`result\` payload contains the E.164-formatted \`normalized\_value\`, the \`phone\_type\` (one of \`FIXED\_LINE\`, \`MOBILE\`, \`FIXED\_LINE\_OR\_MOBILE\`, \`TOLL\_FREE\`, \`PREMIUM\_RATE\`, \`SHARED\_COST\`, \`VOIP\`, \`PERSONAL\_NUMBER\`, \`PAGER\`, \`UAN\`, \`VOICEMAIL\`), the \`carrier\` name, the \`timezone\`, the \`zone\` (ISO 3166-1 alpha-2 country code), the \`area\` (country display name), and validity / completeness / consistency / accuracy scores.\
> \
> Reading a result starts no work and adds no charge, but it finalizes the job's accounting once: the job's charge is settled when its result is first delivered, and refunded when the job failed, exceeded its time limit or produced no usable result — a job past its time limit is marked failed by that read. Repeating the call returns the same answer and never charges again.

```json
{"openapi":"3.1.0","info":{"title":"Delpha Data Quality API","version":"2.3.2"},"tags":[{"name":"Phone","description":"The phone operations normalize a submitted number to E.164 and report its line type, carrier, country and timezone. The number is normalized, then verified to confirm that the line exists and to classify it — never to identify whose number it is.\n\nThree dimensions are scored:\n- **Completeness:** whether a phone number is present and structurally complete.\n- **Validity:** whether the number follows the national or international format and is neither blacklisted nor clearly invalid.\n- **Consistency:** the original input compared with its normalized international form.\n\nA `country` or `country_code` supplies the numbering plan for a national-format number; a number in E.164 form carries its own. These operations validate and normalize only — they do not search for a number."}],"servers":[{"url":"https://api.delpha.io","description":"Production API server"}],"security":[{"oauth2":["api/access"]},{"apiKey":[]}],"components":{"securitySchemes":{"oauth2":{"type":"oauth2","description":"OAuth 2.0 client-credentials flow (machine-to-machine). Token URL: https://secure.delpha.io/oauth2/token. The `api/access` scope is all-access: it grants every operation this document publishes. Per-use-case scoping, per-key credit ceilings and expiry are properties of an API key, not of this flow — use an API key when a credential must be restricted.","flows":{"clientCredentials":{"tokenUrl":"https://secure.delpha.io/oauth2/token","scopes":{"api/access":"Access to the public API"}}}},"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"dlp_","description":"Native Delpha API key. Send it as an HTTP bearer token (`Authorization: Bearer dlp_...`). Keys are issued from the Delpha app (Developer → API keys) and are billed per team against the team's credit balance."}},"schemas":{"PhonePullOutput":{"additionalProperties":true,"description":"Response after retrieving the status and result of an phone quality job.","properties":{"code":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Stable machine-readable status/error code (see ErrorCode).","title":"Code"},"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The API Gateway request id, for support correlation.","title":"Request Id"},"doc_url":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Link to error documentation (present when published).","title":"Doc Url"},"message":{"description":"A human-readable message describing the current state or outcome of the job.","title":"Message","type":"string"},"status":{"description":"The status of the process","enum":["SUCCESS","ERROR"],"title":"Status","type":"string"},"status_code":{"description":"The HTTP status code representing the job's current state.","title":"Status Code","type":"integer"},"result":{"anyOf":[{"$ref":"#/components/schemas/PhoneModel"},{"type":"null"}],"default":null,"description":"The result of the phone quality job"},"process_time":{"anyOf":[{"minimum":0,"type":"number"},{"type":"null"}],"default":null,"description":"The total time taken to process the job, in seconds.","title":"Process Time"}},"required":["message","status","status_code"],"title":"PhonePullOutput","type":"object"},"PhoneModel":{"additionalProperties":true,"description":"Model representing the details of an phone field.","properties":{"data_type":{"$ref":"#/components/schemas/DataType","description":"The type of data for this field"},"normalized_value":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Normalized phone value.","title":"Normalized Value"},"phone_type":{"anyOf":[{"$ref":"#/components/schemas/PhoneType"},{"type":"null"}],"default":null,"description":"The type of phone number"},"carrier":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The carrier of the phone number","title":"Carrier"},"timezone":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The timezone of the phone number","title":"Timezone"},"zone":{"anyOf":[{"$ref":"#/components/schemas/Zone"},{"type":"null"}],"default":null,"description":"The zone of the phone number"},"area":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The area of the phone number","title":"Area"},"activity_score":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"description":"How recently the line has shown activity, on the second-tier line-type provider's own 0-100 scale. `null` whenever that provider was not consulted — which is the common case, since it is called only to resolve an ambiguous line type.","title":"Activity Score"},"is_prepaid":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":null,"description":"Whether the number is a prepaid line, from the second-tier line-type provider. `null` whenever that provider was not consulted — see `activity_score`.","title":"Is Prepaid"},"scores":{"$ref":"#/components/schemas/PhoneScoresModel","description":"Scores for the field"}},"required":["data_type","scores"],"title":"PhoneModel","type":"object"},"DataType":{"enum":["email","social_network","name","legal_id","phone","address","website"],"title":"DataType","type":"string"},"PhoneType":{"description":"Enum representing the possible phone number types.","enum":["FIXED_LINE","MOBILE","FIXED_LINE_OR_MOBILE","TOLL_FREE","PREMIUM_RATE","SHARED_COST","VOIP","PERSONAL_NUMBER","PAGER","UAN","VOICEMAIL"],"title":"PhoneType","type":"string"},"Zone":{"description":"Enum representing the possible country/zone codes for phone numbers\n(ISO 3166-1 alpha-2).","enum":["AC","AD","AE","AF","AG","AI","AL","AM","AO","AR","AS","AT","AU","AW","AX","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ","BL","BM","BN","BO","BQ","BR","BS","BT","BW","BY","BZ","CA","CC","CD","CF","CG","CH","CI","CK","CL","CM","CN","CO","CR","CU","CV","CW","CX","CY","CZ","DE","DJ","DK","DM","DO","DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ","FK","FM","FO","FR","GA","GB","GD","GE","GF","GG","GH","GI","GL","GM","GN","GP","GQ","GR","GT","GU","GW","GY","HK","HN","HR","HT","HU","ID","IE","IL","IM","IN","IO","IQ","IR","IS","IT","JE","JM","JO","JP","KE","KG","KH","KI","KM","KN","KP","KR","KW","KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT","LU","LV","LY","MA","MC","MD","ME","MF","MG","MH","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI","NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF","PG","PH","PK","PL","PM","PR","PS","PT","PW","PY","QA","RE","RO","RS","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO","SR","SS","ST","SV","SX","SY","SZ","TA","TC","TD","TG","TH","TJ","TK","TL","TM","TN","TO","TR","TT","TV","TW","TZ","UA","UG","US","UY","UZ","VA","VC","VE","VG","VI","VN","VU","WF","WS","XK","YE","YT","ZA","ZM","ZW"],"title":"Zone","type":"string"},"PhoneScoresModel":{"description":"The four shared score dimensions plus the one only phone produces.\n\n``accuracy`` is inherited and is always ``-1`` (\"not assessed\") on this use\ncase: the phone backend emits no accuracy dimension.","properties":{"accuracy":{"default":-1,"description":"Accuracy score","maximum":1,"minimum":-1,"title":"Accuracy","type":"number"},"validity":{"default":-1,"description":"Validity score","maximum":1,"minimum":-1,"title":"Validity","type":"integer"},"completeness":{"default":0,"description":"Completeness score","maximum":1,"minimum":0,"title":"Completeness","type":"number"},"consistency":{"default":0,"description":"Consistency score","maximum":1,"minimum":0,"title":"Consistency","type":"integer"},"confidence":{"default":0,"description":"How much to trust the normalization, from the phone library's own reliability verdict: `1` (high), `0.5` (medium) or `0` (low, or not reported). It rates the PARSE, not whether the line is reachable — that is `validity`.","maximum":1,"minimum":0,"title":"Confidence","type":"number"}},"title":"PhoneScoresModel","type":"object"},"Error":{"type":"object","title":"Error","description":"Typed error envelope emitted by the API for handler-level 4xx/5xx responses. Extends the success envelope with a stable machine-readable `code` and a correlation `request_id`.","properties":{"status":{"type":"string","enum":["ERROR"],"description":"Always \"ERROR\" for an error response."},"status_code":{"type":"integer","description":"The HTTP status code of the response."},"message":{"type":"string","description":"Human-readable, PII-free error message."},"code":{"type":"string","enum":["validation_error","unauthorized","payment_required","not_found","conflict","rate_limited","backend_error","backend_timeout","service_unavailable","job_timeout","internal_error","unsupported_registry","idempotency_key_reuse","idempotency_in_progress","idempotency_key_invalid"],"description":"Stable machine-readable error code. `ok` and `job_running` are the success-side codes and never appear in an error body."},"request_id":{"type":"string","description":"API Gateway request id, for support correlation. The same value is returned in the `x-amzn-RequestId` response header on every response, including the 401/403/429 API Gateway generates itself."},"doc_url":{"type":"string","description":"Link to error documentation. Present when error documentation is published."}},"required":["status","status_code","message"],"additionalProperties":true},"GatewayError":{"type":"object","title":"GatewayError","description":"Error body returned by API Gateway itself, before a handler runs: 401 (no or invalid credential), 403 (the API key's scopes do not cover this operation, or the path is not routed here) and 429 (stage throttle; carries `Retry-After`). It has no `code` and no `request_id`; correlate it through the `x-amzn-RequestId` response header instead. A 401 a handler emits after the gateway admitted the request carries the full `Error` envelope.","properties":{"status":{"type":"string","enum":["ERROR"],"description":"Always \"ERROR\"."},"status_code":{"type":"integer","description":"The HTTP status code of the response (401, 403 or 429)."},"message":{"type":"string","description":"Human-readable error message."}},"required":["message"],"additionalProperties":true}},"headers":{"RateLimitLimit":{"description":"Your request allowance for this operation in the current 60-second window. It is counted per credential owner AND per operation: spending it on one operation does not reduce what you may send to another.","schema":{"type":"string"}},"RateLimitRemaining":{"description":"Requests still available to you on this operation in the current window. `0` on the request that exhausts it and on the refusals that follow.","schema":{"type":"string"}},"RateLimitReset":{"description":"When the current window resets, as **UTC epoch seconds** — an absolute instant, not a number of seconds to wait. Use `Retry-After` for the delay.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of the allowance, as a structured field of `draft-ietf-httpapi-ratelimit-headers` (an Internet-Draft, not a published standard): `\"<policy>\";r=<remaining>;t=<seconds to reset>`. Exactly one policy governs a request, so the list always carries one member.","schema":{"type":"string"}},"RateLimitPolicy":{"description":"The allowance that governed this request, as a structured field of `draft-ietf-httpapi-ratelimit-headers` (an Internet-Draft, not a published standard): `\"<policy>\";q=<quota>;w=<window seconds>`. `submit` governs submit and synchronous operations, `poll` the poll operations; the policy for each operation is also published in its `x-rate-limit`.","schema":{"type":"string"}},"DelphaMode":{"description":"Present on every response the API renders to a **test-mode** key, and on no other: absence means the call was live. A test-mode call is answered from the published fixture table, reserves no credits and reaches no verification backend. The 401, 403 and shared-floor 429 the gateway answers before the API sees the request carry no per-request headers, so they never carry this one. See the *Test mode* section of this document.","schema":{"type":"string","enum":["sandbox"]}}}},"paths":{"/v1/phone/{job_id}":{"get":{"tags":["Phone"],"summary":"Get phone validation result","description":"Fetch the result of a previously submitted `findAndValidatePhone` job by `job_id`. On success, the `result` payload contains the E.164-formatted `normalized_value`, the `phone_type` (one of `FIXED_LINE`, `MOBILE`, `FIXED_LINE_OR_MOBILE`, `TOLL_FREE`, `PREMIUM_RATE`, `SHARED_COST`, `VOIP`, `PERSONAL_NUMBER`, `PAGER`, `UAN`, `VOICEMAIL`), the `carrier` name, the `timezone`, the `zone` (ISO 3166-1 alpha-2 country code), the `area` (country display name), and validity / completeness / consistency / accuracy scores.\n\nReading a result starts no work and adds no charge, but it finalizes the job's accounting once: the job's charge is settled when its result is first delivered, and refunded when the job failed, exceeded its time limit or produced no usable result — a job past its time limit is marked failed by that read. Repeating the call returns the same answer and never charges again.","operationId":"getPhoneResult","responses":{"200":{"description":"Job succeeded — the result payload is populated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhonePullOutput"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"202":{"description":"Accepted / still running — poll the result route with the returned job_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhonePullOutput"}}},"headers":{"Location":{"description":"Poll URL for this job (present when the response carries a job_id).","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait before polling again.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"400":{"description":"Validation error — the request body or query parameters were rejected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"401":{"description":"Authentication failed — invalid or missing OAuth 2.0 token or API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayError"}}},"headers":{}},"403":{"description":"Forbidden — the credential is valid but not allowed here: the API key's scopes do not cover this operation, or the operation is not routed in this environment (API Gateway then answers `Missing Authentication Token`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayError"}}},"headers":{}},"404":{"description":"Not found — no job exists for this id, or it belongs to another team.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"409":{"description":"Conflict — the credit reservation expired before delivery; resubmit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"429":{"description":"Too many requests — one of two limits was exceeded. Your allowance for this operation in the current 60-second window: an `Error` body with `code: rate_limited`, `Retry-After` set to the seconds left in the window, and the quota headers with `X-RateLimit-Remaining: 0`; nothing is charged. Or the per-operation floor every caller of that operation shares: a `GatewayError` body with `Retry-After: 1` and no quota headers, refused before the API sees the request.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/GatewayError"}]}}},"headers":{"Retry-After":{"description":"Seconds to wait before retrying: the time left in your current window on the allowance refusal, and `1` on the shared per-operation floor.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"RateLimit":{"$ref":"#/components/headers/RateLimit","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no per-request headers."}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"503":{"description":"Service unavailable — a downstream dependency was unreachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"504":{"description":"Gateway timeout — the job exceeded its processing ceiling.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}}},"parameters":[{"name":"job_id","in":"path","required":true,"description":"Opaque job identifier returned by `findAndValidatePhone`.","schema":{"type":"string"}}]}}}}
```

## Submit a batch of phone items

> Submit 1..100 phone items for asynchronous batch processing as ONE job (never a per-item fan-out). Returns a \`job\_id\` immediately (202); poll \`GET /v1/phone/batch/{job\_id}\` for status and results. Each item follows \`PhoneSubmitInput\`; see that component for its fields. It uses the runtime validation of \`POST /v1/phone\`. A payload with 0 items or more than 100 is rejected with 422.\
> \
> A live-mode call reserves credits and creates a job record retained for 10 days; unless your team has set resolution retention to 0, the call and its result are also recorded in your team's resolution history for the window your team configured (up to 30 days on the free tier, 180 on a paid plan). Re-submitting the same input starts a new job and a new charge, unless the request carries an \`Idempotency-Key\` already used with the same body within the last 24 hours — that returns the original \`job\_id\` and reserves nothing. Test-mode calls are answered from the published fixture table and reserve nothing — see the API documentation's \*\*Test mode\*\* section.

```json
{"openapi":"3.1.0","info":{"title":"Delpha Data Quality API","version":"2.3.2"},"tags":[{"name":"Phone","description":"The phone operations normalize a submitted number to E.164 and report its line type, carrier, country and timezone. The number is normalized, then verified to confirm that the line exists and to classify it — never to identify whose number it is.\n\nThree dimensions are scored:\n- **Completeness:** whether a phone number is present and structurally complete.\n- **Validity:** whether the number follows the national or international format and is neither blacklisted nor clearly invalid.\n- **Consistency:** the original input compared with its normalized international form.\n\nA `country` or `country_code` supplies the numbering plan for a national-format number; a number in E.164 form carries its own. These operations validate and normalize only — they do not search for a number."}],"servers":[{"url":"https://api.delpha.io","description":"Production API server"}],"security":[{"oauth2":["api/access"]},{"apiKey":[]}],"components":{"securitySchemes":{"oauth2":{"type":"oauth2","description":"OAuth 2.0 client-credentials flow (machine-to-machine). Token URL: https://secure.delpha.io/oauth2/token. The `api/access` scope is all-access: it grants every operation this document publishes. Per-use-case scoping, per-key credit ceilings and expiry are properties of an API key, not of this flow — use an API key when a credential must be restricted.","flows":{"clientCredentials":{"tokenUrl":"https://secure.delpha.io/oauth2/token","scopes":{"api/access":"Access to the public API"}}}},"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"dlp_","description":"Native Delpha API key. Send it as an HTTP bearer token (`Authorization: Bearer dlp_...`). Keys are issued from the Delpha app (Developer → API keys) and are billed per team against the team's credit balance."}},"schemas":{"BatchSubmitOutput":{"additionalProperties":true,"description":"Response after submitting a batch quality job.","properties":{"code":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Stable machine-readable status/error code (see ErrorCode).","title":"Code"},"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The API Gateway request id, for support correlation.","title":"Request Id"},"doc_url":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Link to error documentation (present when published).","title":"Doc Url"},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The job ID. Required if status <= 300.","title":"Job Id"},"item_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"description":"Number of items accepted in the batch (present on success).","title":"Item Count"},"message":{"description":"The message to return","title":"Message","type":"string"},"status":{"description":"The status of the process","enum":["SUCCESS","ERROR"],"title":"Status","type":"string"},"status_code":{"description":"The status code to return","title":"Status Code","type":"integer"}},"required":["message","status","status_code"],"title":"BatchSubmitOutput","type":"object"},"GatewayError":{"type":"object","title":"GatewayError","description":"Error body returned by API Gateway itself, before a handler runs: 401 (no or invalid credential), 403 (the API key's scopes do not cover this operation, or the path is not routed here) and 429 (stage throttle; carries `Retry-After`). It has no `code` and no `request_id`; correlate it through the `x-amzn-RequestId` response header instead. A 401 a handler emits after the gateway admitted the request carries the full `Error` envelope.","properties":{"status":{"type":"string","enum":["ERROR"],"description":"Always \"ERROR\"."},"status_code":{"type":"integer","description":"The HTTP status code of the response (401, 403 or 429)."},"message":{"type":"string","description":"Human-readable error message."}},"required":["message"],"additionalProperties":true},"Error":{"type":"object","title":"Error","description":"Typed error envelope emitted by the API for handler-level 4xx/5xx responses. Extends the success envelope with a stable machine-readable `code` and a correlation `request_id`.","properties":{"status":{"type":"string","enum":["ERROR"],"description":"Always \"ERROR\" for an error response."},"status_code":{"type":"integer","description":"The HTTP status code of the response."},"message":{"type":"string","description":"Human-readable, PII-free error message."},"code":{"type":"string","enum":["validation_error","unauthorized","payment_required","not_found","conflict","rate_limited","backend_error","backend_timeout","service_unavailable","job_timeout","internal_error","unsupported_registry","idempotency_key_reuse","idempotency_in_progress","idempotency_key_invalid"],"description":"Stable machine-readable error code. `ok` and `job_running` are the success-side codes and never appear in an error body."},"request_id":{"type":"string","description":"API Gateway request id, for support correlation. The same value is returned in the `x-amzn-RequestId` response header on every response, including the 401/403/429 API Gateway generates itself."},"doc_url":{"type":"string","description":"Link to error documentation. Present when error documentation is published."}},"required":["status","status_code","message"],"additionalProperties":true},"PhoneBatchSubmitInput":{"description":"Batch envelope for `POST /v1/phone/batch`: `{\"items\": [...]}` (1..100 items, 422 beyond). Each item follows `PhoneSubmitInput`; see that component for its fields. It uses the runtime validation of `POST /v1/phone`.","properties":{"items":{"description":"The batch items (1..100), each a single-route input payload.","items":{"$ref":"#/components/schemas/PhoneSubmitInput"},"maxItems":100,"minItems":1,"title":"Items","type":"array"}},"required":["items"],"title":"PhoneBatchSubmitInput","type":"object"},"PhoneSubmitInput":{"description":"Payload to submit a new phone quality job.\n\n**Important**: For proper normalization and country detection, either:\n- Provide a phone number with international code (e.g., \"+33672557378\"), OR\n- Provide the phone number along with `country` or `country_code`\n\nIf none of these conditions are met, the system may not be able to properly\ndetect the country and apply the related normalization.","properties":{"phone":{"description":"Phone number to validate. E.164 form (e.g. `+33672557378`) is preferred and self-locating; national form (e.g. `0672557378`) requires `country` or `country_code`. Required.","title":"Phone","type":"string"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Country display name in English (e.g. `France`). Supplies the numbering plan when `phone` is not in E.164 form; either this or `country_code` does, and `country_code` wins when both are set.","title":"Country"},"country_code":{"anyOf":[{"type":"string","pattern":"^[A-Z]{2}$"},{"type":"null"}],"default":null,"description":"ISO 3166-1 alpha-2 country code (e.g. `FR`, `US`, `GB`). Preferred over `country` when known. Required when `phone` is not in E.164 form.","title":"Country Code"}},"required":["phone"],"title":"PhoneSubmitInput","type":"object"}},"headers":{"RateLimitLimit":{"description":"Your request allowance for this operation in the current 60-second window. It is counted per credential owner AND per operation: spending it on one operation does not reduce what you may send to another.","schema":{"type":"string"}},"RateLimitRemaining":{"description":"Requests still available to you on this operation in the current window. `0` on the request that exhausts it and on the refusals that follow.","schema":{"type":"string"}},"RateLimitReset":{"description":"When the current window resets, as **UTC epoch seconds** — an absolute instant, not a number of seconds to wait. Use `Retry-After` for the delay.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of the allowance, as a structured field of `draft-ietf-httpapi-ratelimit-headers` (an Internet-Draft, not a published standard): `\"<policy>\";r=<remaining>;t=<seconds to reset>`. Exactly one policy governs a request, so the list always carries one member.","schema":{"type":"string"}},"RateLimitPolicy":{"description":"The allowance that governed this request, as a structured field of `draft-ietf-httpapi-ratelimit-headers` (an Internet-Draft, not a published standard): `\"<policy>\";q=<quota>;w=<window seconds>`. `submit` governs submit and synchronous operations, `poll` the poll operations; the policy for each operation is also published in its `x-rate-limit`.","schema":{"type":"string"}},"DelphaMode":{"description":"Present on every response the API renders to a **test-mode** key, and on no other: absence means the call was live. A test-mode call is answered from the published fixture table, reserves no credits and reaches no verification backend. The 401, 403 and shared-floor 429 the gateway answers before the API sees the request carry no per-request headers, so they never carry this one. See the *Test mode* section of this document.","schema":{"type":"string","enum":["sandbox"]}}}},"paths":{"/v1/phone/batch":{"post":{"tags":["Phone"],"summary":"Submit a batch of phone items","description":"Submit 1..100 phone items for asynchronous batch processing as ONE job (never a per-item fan-out). Returns a `job_id` immediately (202); poll `GET /v1/phone/batch/{job_id}` for status and results. Each item follows `PhoneSubmitInput`; see that component for its fields. It uses the runtime validation of `POST /v1/phone`. A payload with 0 items or more than 100 is rejected with 422.\n\nA live-mode call reserves credits and creates a job record retained for 10 days; unless your team has set resolution retention to 0, the call and its result are also recorded in your team's resolution history for the window your team configured (up to 30 days on the free tier, 180 on a paid plan). Re-submitting the same input starts a new job and a new charge, unless the request carries an `Idempotency-Key` already used with the same body within the last 24 hours — that returns the original `job_id` and reserves nothing. Test-mode calls are answered from the published fixture table and reserve nothing — see the API documentation's **Test mode** section.","operationId":"submitPhoneBatch","responses":{"202":{"description":"Accepted / still running — poll the result route with the returned job_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchSubmitOutput"}}},"headers":{"Location":{"description":"Poll URL for this job (present when the response carries a job_id).","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait before polling again.","schema":{"type":"integer"}},"Idempotent-Replayed":{"description":"Set to 'true' when this 202 replays a previously accepted request with the same Idempotency-Key (no new job, no additional charge).","schema":{"type":"string"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"401":{"description":"Authentication failed — invalid or missing OAuth 2.0 token or API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayError"}}},"headers":{}},"402":{"description":"Payment required — the team has insufficient credits.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"403":{"description":"Forbidden — the credential is valid but not allowed here: the API key's scopes do not cover this operation, or the operation is not routed in this environment (API Gateway then answers `Missing Authentication Token`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayError"}}},"headers":{}},"409":{"description":"Conflict — the `Idempotency-Key` was already used with a different request body (`idempotency_key_reuse`: a new key admits the new body, the original body replays the original job), or the original request with this key is still being processed (`idempotency_in_progress`: the same request answers with the original job once that one is admitted).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"422":{"description":"Unprocessable entity — the request is well-formed but cannot be processed: a documented size/shape bound was violated (`validation_error`), or a legal-ID request names a registry / country Delpha cannot query (`unsupported_registry` — refused before any credit is reserved; the message lists the supported countries).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"429":{"description":"Too many requests — one of two limits was exceeded. Your allowance for this operation in the current 60-second window: an `Error` body with `code: rate_limited`, `Retry-After` set to the seconds left in the window, and the quota headers with `X-RateLimit-Remaining: 0`; nothing is charged. Or the per-operation floor every caller of that operation shares: a `GatewayError` body with `Retry-After: 1` and no quota headers, refused before the API sees the request.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/GatewayError"}]}}},"headers":{"Retry-After":{"description":"Seconds to wait before retrying: the time left in your current window on the allowance refusal, and `1` on the shared per-operation floor.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"RateLimit":{"$ref":"#/components/headers/RateLimit","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no per-request headers."}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"502":{"description":"Bad gateway — the upstream service returned an error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneBatchSubmitInput"}}}}}}}}
```

## Get phone batch job status

> Poll a batch of phone items submitted via \`POST /v1/phone/batch\`. 202 while running (with \`item\_count\`/\`processed\_count\` progress); 200 once SUCCEEDED (with \`counts\`, a presigned \`result\_url\`, and \`result\_data\` inlined when <=1MB); 409 if the credit reservation expired before delivery (resubmit); 500 if the job failed; 504 past the 24h ceiling.\
> \
> Reading a result starts no work and adds no charge, but it finalizes the job's accounting once: the job's charge is settled when its result is first delivered, and refunded when the job failed, exceeded its time limit or produced no usable result — a job past its time limit is marked failed by that read. Repeating the call returns the same answer and never charges again.

```json
{"openapi":"3.1.0","info":{"title":"Delpha Data Quality API","version":"2.3.2"},"tags":[{"name":"Phone","description":"The phone operations normalize a submitted number to E.164 and report its line type, carrier, country and timezone. The number is normalized, then verified to confirm that the line exists and to classify it — never to identify whose number it is.\n\nThree dimensions are scored:\n- **Completeness:** whether a phone number is present and structurally complete.\n- **Validity:** whether the number follows the national or international format and is neither blacklisted nor clearly invalid.\n- **Consistency:** the original input compared with its normalized international form.\n\nA `country` or `country_code` supplies the numbering plan for a national-format number; a number in E.164 form carries its own. These operations validate and normalize only — they do not search for a number."}],"servers":[{"url":"https://api.delpha.io","description":"Production API server"}],"security":[{"oauth2":["api/access"]},{"apiKey":[]}],"components":{"securitySchemes":{"oauth2":{"type":"oauth2","description":"OAuth 2.0 client-credentials flow (machine-to-machine). Token URL: https://secure.delpha.io/oauth2/token. The `api/access` scope is all-access: it grants every operation this document publishes. Per-use-case scoping, per-key credit ceilings and expiry are properties of an API key, not of this flow — use an API key when a credential must be restricted.","flows":{"clientCredentials":{"tokenUrl":"https://secure.delpha.io/oauth2/token","scopes":{"api/access":"Access to the public API"}}}},"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"dlp_","description":"Native Delpha API key. Send it as an HTTP bearer token (`Authorization: Bearer dlp_...`). Keys are issued from the Delpha app (Developer → API keys) and are billed per team against the team's credit balance."}},"schemas":{"PhoneBatchPullOutput":{"additionalProperties":true,"description":"Response for `GET /v1/phone/batch/{job_id}`. Each `result_data.items[]` entry is keyed to its request item by `dispatch_key` (`{\"record_id\": \"<job_id>#<index>\"}`). On success its `result` is the SAME object `GET /v1/phone/{job_id}` returns for a single item. On failure it carries an `error` string instead of `result`.","properties":{"code":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Stable machine-readable status/error code (see ErrorCode).","title":"Code"},"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The API Gateway request id, for support correlation.","title":"Request Id"},"doc_url":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Link to error documentation (present when published).","title":"Doc Url"},"message":{"description":"A human-readable message describing the current state or outcome of the job.","title":"Message","type":"string"},"status":{"description":"The status of the process","enum":["SUCCESS","ERROR"],"title":"Status","type":"string"},"status_code":{"description":"The HTTP status code representing the job's current state.","title":"Status Code","type":"integer"},"counts":{"anyOf":[{"$ref":"#/components/schemas/CountsModel"},{"type":"null"}],"default":null,"description":"Item-level outcome counts (present only once the job has completed)."},"result_url":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Presigned URL (1h expiry) to download the full batch result JSON from S3.","title":"Result Url"},"result_data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"description":"The full batch result JSON, inlined (opaque). Present only when status_code == 200 and the result file is <=1MB; otherwise omitted (fetch `result_url` instead). Shape: `{job_id, usecase, counts, items: [...]}`; its `result` is the SAME object `GET /v1/phone/{job_id}` returns for a single item.","title":"Result Data"},"item_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"description":"Total number of items in the batch (present while the job is running).","title":"Item Count"},"processed_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"description":"Number of items processed so far (present while the job is running; may be absent early in the job's lifecycle).","title":"Processed Count"},"process_time":{"anyOf":[{"minimum":0,"type":"number"},{"type":"null"}],"default":null,"description":"The total time taken to process the job, in seconds.","title":"Process Time"}},"required":["message","status","status_code"],"title":"PhoneBatchPullOutput","type":"object"},"CountsModel":{"description":"Item-level outcome counts for a completed batch job.","properties":{"total":{"description":"Total number of items submitted.","title":"Total","type":"integer"},"succeeded":{"description":"Number of items that succeeded.","title":"Succeeded","type":"integer"},"failed":{"description":"Number of items that failed.","title":"Failed","type":"integer"}},"required":["total","succeeded","failed"],"title":"CountsModel","type":"object"},"Error":{"type":"object","title":"Error","description":"Typed error envelope emitted by the API for handler-level 4xx/5xx responses. Extends the success envelope with a stable machine-readable `code` and a correlation `request_id`.","properties":{"status":{"type":"string","enum":["ERROR"],"description":"Always \"ERROR\" for an error response."},"status_code":{"type":"integer","description":"The HTTP status code of the response."},"message":{"type":"string","description":"Human-readable, PII-free error message."},"code":{"type":"string","enum":["validation_error","unauthorized","payment_required","not_found","conflict","rate_limited","backend_error","backend_timeout","service_unavailable","job_timeout","internal_error","unsupported_registry","idempotency_key_reuse","idempotency_in_progress","idempotency_key_invalid"],"description":"Stable machine-readable error code. `ok` and `job_running` are the success-side codes and never appear in an error body."},"request_id":{"type":"string","description":"API Gateway request id, for support correlation. The same value is returned in the `x-amzn-RequestId` response header on every response, including the 401/403/429 API Gateway generates itself."},"doc_url":{"type":"string","description":"Link to error documentation. Present when error documentation is published."}},"required":["status","status_code","message"],"additionalProperties":true},"GatewayError":{"type":"object","title":"GatewayError","description":"Error body returned by API Gateway itself, before a handler runs: 401 (no or invalid credential), 403 (the API key's scopes do not cover this operation, or the path is not routed here) and 429 (stage throttle; carries `Retry-After`). It has no `code` and no `request_id`; correlate it through the `x-amzn-RequestId` response header instead. A 401 a handler emits after the gateway admitted the request carries the full `Error` envelope.","properties":{"status":{"type":"string","enum":["ERROR"],"description":"Always \"ERROR\"."},"status_code":{"type":"integer","description":"The HTTP status code of the response (401, 403 or 429)."},"message":{"type":"string","description":"Human-readable error message."}},"required":["message"],"additionalProperties":true}},"headers":{"RateLimitLimit":{"description":"Your request allowance for this operation in the current 60-second window. It is counted per credential owner AND per operation: spending it on one operation does not reduce what you may send to another.","schema":{"type":"string"}},"RateLimitRemaining":{"description":"Requests still available to you on this operation in the current window. `0` on the request that exhausts it and on the refusals that follow.","schema":{"type":"string"}},"RateLimitReset":{"description":"When the current window resets, as **UTC epoch seconds** — an absolute instant, not a number of seconds to wait. Use `Retry-After` for the delay.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of the allowance, as a structured field of `draft-ietf-httpapi-ratelimit-headers` (an Internet-Draft, not a published standard): `\"<policy>\";r=<remaining>;t=<seconds to reset>`. Exactly one policy governs a request, so the list always carries one member.","schema":{"type":"string"}},"RateLimitPolicy":{"description":"The allowance that governed this request, as a structured field of `draft-ietf-httpapi-ratelimit-headers` (an Internet-Draft, not a published standard): `\"<policy>\";q=<quota>;w=<window seconds>`. `submit` governs submit and synchronous operations, `poll` the poll operations; the policy for each operation is also published in its `x-rate-limit`.","schema":{"type":"string"}},"DelphaMode":{"description":"Present on every response the API renders to a **test-mode** key, and on no other: absence means the call was live. A test-mode call is answered from the published fixture table, reserves no credits and reaches no verification backend. The 401, 403 and shared-floor 429 the gateway answers before the API sees the request carry no per-request headers, so they never carry this one. See the *Test mode* section of this document.","schema":{"type":"string","enum":["sandbox"]}}}},"paths":{"/v1/phone/batch/{job_id}":{"get":{"tags":["Phone"],"summary":"Get phone batch job status","description":"Poll a batch of phone items submitted via `POST /v1/phone/batch`. 202 while running (with `item_count`/`processed_count` progress); 200 once SUCCEEDED (with `counts`, a presigned `result_url`, and `result_data` inlined when <=1MB); 409 if the credit reservation expired before delivery (resubmit); 500 if the job failed; 504 past the 24h ceiling.\n\nReading a result starts no work and adds no charge, but it finalizes the job's accounting once: the job's charge is settled when its result is first delivered, and refunded when the job failed, exceeded its time limit or produced no usable result — a job past its time limit is marked failed by that read. Repeating the call returns the same answer and never charges again.","operationId":"getPhoneBatchResult","responses":{"200":{"description":"Job succeeded — the result payload is populated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneBatchPullOutput"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"202":{"description":"Accepted / still running — poll the result route with the returned job_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneBatchPullOutput"}}},"headers":{"Location":{"description":"Poll URL for this job (present when the response carries a job_id).","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait before polling again.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"400":{"description":"Validation error — the request body or query parameters were rejected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"401":{"description":"Authentication failed — invalid or missing OAuth 2.0 token or API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayError"}}},"headers":{}},"403":{"description":"Forbidden — the credential is valid but not allowed here: the API key's scopes do not cover this operation, or the operation is not routed in this environment (API Gateway then answers `Missing Authentication Token`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayError"}}},"headers":{}},"404":{"description":"Not found — no job exists for this id, or it belongs to another team.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"409":{"description":"Conflict — the credit reservation expired before delivery; resubmit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"429":{"description":"Too many requests — one of two limits was exceeded. Your allowance for this operation in the current 60-second window: an `Error` body with `code: rate_limited`, `Retry-After` set to the seconds left in the window, and the quota headers with `X-RateLimit-Remaining: 0`; nothing is charged. Or the per-operation floor every caller of that operation shares: a `GatewayError` body with `Retry-After: 1` and no quota headers, refused before the API sees the request.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/GatewayError"}]}}},"headers":{"Retry-After":{"description":"Seconds to wait before retrying: the time left in your current window on the allowance refusal, and `1` on the shared per-operation floor.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"RateLimit":{"$ref":"#/components/headers/RateLimit","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no quota headers."},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode","description":"Present on the allowance refusal only. The shared per-operation floor is refused by the gateway, whose responses carry no per-request headers."}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"503":{"description":"Service unavailable — a downstream dependency was unreachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}},"504":{"description":"Gateway timeout — the job exceeded its processing ceiling.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"X-Delpha-Mode":{"$ref":"#/components/headers/DelphaMode"}}}},"parameters":[{"name":"job_id","in":"path","required":true,"description":"The unique identifier of the phone batch job.","schema":{"type":"string"}}]}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://help.delpha.io/delpha-api-and-mcp/api/phone.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `build a script that syncs our docs to a CMS` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
