---
title: "Submit Certificate"
method: POST
path: "/tax/certificates"
---

# Submit Certificate

`POST /tax/certificates`

## Request

- Base URL: `https://api.numeralhq.com`
- URL: `https://api.numeralhq.com/tax/certificates`
- Auth: HTTP bearer

Submit a certificate document for asynchronous processing. Numeral downloads the document from the `https` URL you provide, stores it, and classifies the certificate type and jurisdiction from the document itself. Returns a `tax.certificate_upload` you can poll. Live-only — `sk_test_*` keys are rejected with `TESTMODE_NOT_SUPPORTED`.

## Headers

- `X-API-Version` '2026-03-01', required

## Request body

- CertificateUploadRequest
  - `document_url` string, uri, required: An `https` URL that Numeral's servers can `GET` — typically a presigned URL to your own storage. Fetched server-side. The file must be a PDF, PNG, or JPEG (detected from content, not extension or `Content-Type`), at most 10 MB, reachable within 3 redirects (each hop must also be `https`).
  - `file_name` string: Display name for the document (1–255 characters). Defaults to the URL's basename when it has an extension, otherwise to a generic name derived from the detected file type.
  - `customer` CertificateUploadCustomerInput: Pre-maps the resulting certificate to a customer. Supply at least one of `id` or `reference_customer_id`. Either alone is sufficient; if both are present and resolve to different customers the request fails with `BUYER_IDENTITY_CONFLICT` (409) and nothing is written.
    - `id` string: Existing Numeral customer id. An unknown id returns `CUSTOMER_NOT_FOUND` (404).
    - `reference_customer_id` string: Your own identifier for the customer. An unknown value creates the customer, so pre-mapping works before the customer exists in Numeral.

## Response `201`

Upload accepted. `status` is `processing` or `duplicate`; `certificate_ids` is always empty at this point — poll `GET /tax/certificate-uploads/{upload_id}` for results.

- CertificateUploadResponse: A certificate upload — the asynchronous processing job created by `POST /tax/certificates`.
  - `id` string, required: Upload id, `upl_<number>`. Endpoints that accept an upload id also accept the bare numeric form.
  - `object` string, required: The type of object: `tax.certificate_upload`
  - `status` 'processing' | 'completed' | 'failed' | 'duplicate', required: Processing state of a certificate upload. This is a separate vocabulary from certificate `status` — an upload is a processing job, a certificate is its output. `processing` is the only non-terminal state; `completed` (certificates produced), `failed` (see `error_message`), and `duplicate` (same document already submitted) are terminal.
  - `customer` CertificateUploadCustomer, required: The customer the upload was attributed to at submission.
    - `id` string, required
    - `reference_customer_id` string, nullable, required
  - `certificate_ids` string[], required: Certificates produced by this upload, usable with the certificate endpoints. Empty while `status` is `processing`; populated at `completed`. One document can yield multiple certificates.
  - `error_message` string, nullable, required: Human-readable reason processing failed. Also set on `duplicate` uploads when the original upload had failed, so you can see why re-submitting the same bytes will not help.
  - `created_at` string, date-time, nullable, required
  - `livemode` true, required: Always `true` — these endpoints are live-only.

## Other responses

- `400`: `INVALID_DOCUMENT_URL`, `DOCUMENT_FETCH_FAILED`, `UNSUPPORTED_FILE_TYPE`, `FILE_TOO_LARGE`, `TESTMODE_NOT_SUPPORTED`, or `INVALID_REQUEST` (schema violation or API version below 2026-03-01)
- `404`: `CUSTOMER_NOT_FOUND` — `customer.id` does not exist for this account
- `409`: `BUYER_IDENTITY_CONFLICT` — `customer.id` and `customer.reference_customer_id` resolve to different customers; nothing was written
- `422`: `DOCUMENT_URL_UNAUTHORIZED` — the document host returned 401/403, most commonly an expired presigned URL. Mint a fresh URL and retry.
- `500`: `NUMERAL_ERROR` — the upload was stored but processing could not start. Retry the submission.

## Changes

- **2026-11-03** `f4650eaed11f`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/numeral-com/apis/numeral-api/changes/tax/certificates/post.md)

---

[API](https://skmtc.dev/numeral-com/apis/numeral-api.md) - [All operations](https://skmtc.dev/numeral-com/apis/numeral-api/llms.txt) - [OpenAPI document](https://skmtc.dev/numeral-com/apis/numeral-api/revisions/f4650eaed11f?raw)
