# Documents API reference

> Check signed PDFs.

Source: https://signwith.co/docs/api/documents · Updated 2026-10-02

## Verify a signed PDF

`POST https://app.signwith.co/api/v1/documents/verify`

Read-only keys can call this. Accepts `Idempotency-Key`.

Checks a PDF: whether SignWith issued it (it matches a document completed in SignWith) and
whether each embedded digital signature is valid and the document hasn't been modified since.
Use it when a signed document comes back to you from a third party and you need to confirm
it's authentic. Send the PDF as base64 in a JSON `file` property, or upload it as a
multipart `file`.

This call doesn't change anything, so read-only API keys may use it. Returns
`422 invalid_document` if `file` isn't a PDF.

### Headers

| Name | Type | Description |
| --- | --- | --- |
| `Idempotency-Key` | string | A unique value (e.g. a UUID) that makes retries of this request safe. A successful response is stored for 24 hours and replayed for retries with the same key.  |

### Request body (`application/json`)

- `file` (string, required, base64). The PDF, base64-encoded.

### Request body (`multipart/form-data`)

- `file` (file, required). The PDF file.

### Request sample

cURL:

```bash
curl -X POST https://app.signwith.co/api/v1/documents/verify \
  -H "Authorization: Bearer $SIGNWITH_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "file": "JVBERi0xLjcKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFI+PgplbmRvYmoK..."
}'
```

Node:

```javascript
const response = await fetch('https://app.signwith.co/api/v1/documents/verify', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SIGNWITH_API_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    file: 'JVBERi0xLjcKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFI+PgplbmRvYmoK...',
  }),
})

console.log(response.status, await response.json())
```

Python:

```python
import os
import uuid

import requests

response = requests.post(
    "https://app.signwith.co/api/v1/documents/verify",
    headers={
        "Authorization": f"Bearer {os.environ['SIGNWITH_API_KEY']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "file": "JVBERi0xLjcKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFI+PgplbmRvYmoK...",
    },
)

print(response.status_code, response.json())
```

### Response 200

The verification result.

```json
{
  "object": "document_verification",
  "issued_by_signwith": true,
  "signatures": [
    {
      "signer_name": "SignWith",
      "signed_at": "2026-09-27T10:15:02Z",
      "reason": "Signed by Grace Hopper, Ada Lovelace",
      "valid": true,
      "messages": [
        {
          "type": "info",
          "content": "Signature valid"
        },
        {
          "type": "info",
          "content": "Certificate is trusted"
        }
      ]
    }
  ]
}
```

### Errors

| Status | Meaning |
| --- | --- |
| 400 | The request body is not valid JSON. |
| 401 | The API key is missing, invalid or expired, or its account has been archived. |
| 409 | A request with the same `Idempotency-Key` is still being processed. Retry shortly. |
| 422 | The request is valid JSON but can't be processed. See `error.code`; the codes each endpoint can return are described in the endpoint's description and in the error table above.  |
| 429 | Rate limit exceeded (`rate_limited`, with `Retry-After`); more than 20 `POST /feedback` messages in an hour (`rate_limited`, with `Retry-After`: seconds until the next clock hour); or — for `POST /signature_requests/{id}/remind` — signers were reminded less than an hour ago (`remind_too_soon`, with `Retry-After`).  |
| 500 | Something went wrong on SignWith's side. It's safe to retry with the same `Idempotency-Key`. |

## The document verification object

Anchor: https://signwith.co/docs/api/documents#the-document-verification-object

- `object` (string, required, always `document_verification`).
- `issued_by_signwith` (boolean, required). Whether this exact file is a document completed in SignWith.
- `signatures` (array of objects, required). Every digital signature found in the PDF. Empty if the PDF isn't digitally signed.
  - `signer_name` (string or null, required).
  - `signed_at` (string (date-time) or null, required).
  - `reason` (string or null, required).
  - `valid` (boolean, required). `true` if verification produced no errors.
  - `messages` (array of objects, required). Details from verifying the signature and certificate chain.
    - `type` (string, required, one of `info`, `warning`, `error`).
    - `content` (string, required).
