---
title: "Get Certificate Upload"
method: GET
path: "/tax/certificate-uploads/{upload_id}"
---

# Get Certificate Upload

`GET /tax/certificate-uploads/{upload_id}`

## Request

- Base URL: `https://api.numeralhq.com`
- URL: `https://api.numeralhq.com/tax/certificate-uploads/{upload_id}`
- Auth: HTTP bearer

Retrieve a single certificate upload by id, including a short-lived `document_url` for the originally submitted document. This is the polling endpoint — call it until `status` leaves `processing`. Live-only.

## Path parameters

- `upload_id` string, required

## Headers

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

## Response `200`

Certificate upload detail (includes pre-signed `document_url`)

- CertificateUploadDetailResponse: 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.
  - `document_url` string, uri, nullable, required: Pre-signed URL to the originally submitted document. Valid for one hour — refetch the upload to mint a new URL. May be non-null while `status` is still `processing`. `null` (response still `200`) when no document is attached. Not returned by the list endpoint. Treat as confidential.

## Other responses

- `400`: `TESTMODE_NOT_SUPPORTED` or `INVALID_REQUEST` (API version below 2026-03-01)
- `404`: `CERTIFICATE_UPLOAD_NOT_FOUND` — unknown id, malformed id, or an upload belonging to another account (indistinguishable by design)

## Changes

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

[Operation history](https://skmtc.dev/numeral-com/apis/numeral-api/changes/tax/certificate-uploads/:upload_id/get.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)
