# SendRegistered API (1.0.0)

Upload a PDF, create and pay a registered letter from prepaid business credit, follow tracking with signed webhooks and download the proof certificate. All amounts in API responses are in cents of the stated currency.

- **Base URL:** `https://api.sendregistered.com/v1`
- **OpenAPI 3.1 JSON:** `https://api.sendregistered.com/v1/openapi.json`
- **Documentation:** `https://sendregistered.com/developers/api`

## Quickstart

Send your first registered letter in three calls: upload a PDF, create the letter, follow its events. Keys starting with `sr_test_` run the full flow in the sandbox with test credit and return a sample proof PDF.

1. **Create a test key** — In your account under API keys (`/account/api-keys`), create a `test` key with `read` and `write` scopes.
2. **Upload the PDF** — `POST /v1/documents` with an A4 PDF up to 8 MB (1–20 pages). We check that the address fits the envelope window.
3. **Create and pay the letter** — `POST /v1/letters` with `document_id`, `destination`, `recipient`, `pay: true`, and `terms: true`, plus an `Idempotency-Key` header.
4. **Follow events** — Add a webhook endpoint in `/account/api-keys` to receive `letter.paid`, `letter.printed`, `letter.posted`, `letter.awaiting_pickup`, and `letter.delivered`, signed with `SendRegistered-Signature`.

```bash
curl https://api.sendregistered.com/v1/letters \
  -H "Authorization: Bearer sr_test_4f2a9Kd7Lq" \
  -H "Idempotency-Key: claim-2026-4471" \
  -H "Content-Type: application/json" \
  -d '{
    "document_id": "doc_8Kq2mZ",
    "destination": "DE",
    "product_id": "de-einwurf",
    "sender": {
      "name": "Nordlicht Legal GmbH",
      "street": "Friedrichstraße 114",
      "postal_code": "10117",
      "city": "Berlin",
      "country": "DE"
    },
    "recipient": {
      "name": "Hausverwaltung Berger GmbH",
      "street": "Kastanienallee 12",
      "postal_code": "10435",
      "city": "Berlin",
      "country": "DE"
    },
    "pay": true,
    "terms": true
  }'
```

## Authentication

Authenticate with `Authorization: Bearer sr_live_...` for real letters or `Authorization: Bearer sr_test_...` for free sandbox test letters.

- **Live keys (`sr_live_...`):** Print and post real letters, paid from prepaid EUR credit within the key's daily and monthly EUR limits.
- **Test keys (`sr_test_...`):** Sandbox mode with €1,000 test credit, instant status progression, sample proof PDFs, and test-only webhooks (`livemode: false`).
- **Scopes:** `read` (inspect destinations, quotes, balance, letters, proof) and `write` (upload documents, create/pay/cancel letters).

## Idempotency

Every write request (`POST /v1/documents`, `POST /v1/letters`, `POST /v1/letters/{id}/pay`, `POST /v1/letters/{id}/cancel`) requires an `Idempotency-Key` header (8–200 characters). Repeating the same key with the same body replays the original response; reusing a key with a different body returns `409 idempotency_conflict`.

## Spending Limits

Each API key has an optional daily EUR limit and monthly EUR limit managed in `/account/api-keys`. When a payment would exceed either limit, the API returns `409 limit_exceeded` without charging your balance.

## Webhooks

Configure webhook endpoints in `/account/api-keys`. Subscribable events: `letter.paid`, `letter.printed`, `letter.posted`, `letter.awaiting_pickup`, `letter.delivered`, `letter.failed`, `letter.cancelled`, `balance.low`.

- Every delivery includes `SendRegistered-Signature: t=<unix_seconds>,v1=<hex_hmac_sha256>` computed with the endpoint's signing secret (`whsec_...`) over `"<timestamp>.<raw_json_body>"`.
- Reject signatures where `t` is older than 5 minutes (300 seconds).
- Failed deliveries (`5xx`, `429`, or timeout) are retried with exponential backoff for up to 3 days (72 hours).
- Letters created with a test key (`sr_test_...`) only emit test webhook events (`livemode: false`).

## Endpoints

### `GET /v1/openapi.json` — OpenAPI 3.1 specification

Returns this OpenAPI 3.1 specification generated directly from the handler schemas.

### `GET /v1/destinations` — List destinations and products

Returns all 8 supported destination countries, available postal products, carrier names and prices in cents.

### `POST /v1/quotes` — Calculate a letter quote

Quotes a letter in cents for the authenticated business account, applying EU reverse charge or non-EU VAT rules when configured on the account.

**Request body fields:**

- `destination` (`string` *(required)*) — enum: `DE`, `FR`, `BE`, `ES`, `CH`, `NL`, `GB`, `US`
- `product_id` (`string`)
- `color` (`boolean` (default: `false`))
- `duplex` (`boolean` (default: `false`))
- `acknowledgement` (`boolean` (default: `false`))
- `pages` (`integer` *(required)*)
- `sender_country` (`string`)

### `GET /v1/balance` — Get prepaid credit balance

Returns the account's prepaid credit balance in EUR cents (`paid`, `bonus`, `subscription`, `total` and active `lots`).

### `POST /v1/documents` — Upload a PDF document

Uploads a base64-encoded PDF (up to 8 MB, 1–20 pages) and checks whether the pages fit the printable area and envelope window.

**Request body fields:**

- `filename` (`string` (default: `"letter.pdf"`))
- `content_base64` (`string` *(required)*)

### `POST /v1/letters` — Create (and optionally pay) a registered letter

Creates a registered letter from an uploaded document. Pass `pay: true` and `terms: true` to pay immediately from prepaid credit and queue for printing.

**Request body fields:**

- `destination` (`string` *(required)*) — enum: `DE`, `FR`, `BE`, `ES`, `CH`, `NL`, `GB`, `US`
- `product_id` (`string`)
- `color` (`boolean` (default: `false`))
- `duplex` (`boolean` (default: `false`))
- `acknowledgement` (`boolean` (default: `false`))
- `document_id` (`string` *(required)*)
- `sender` (`object` *(required)*)
  - `name` (`string` *(required)*)
  - `street` (`string` *(required)*)
  - `zip` (`string` *(required)*)
  - `city` (`string` *(required)*)
  - `country` (`string` *(required)*)
  - `state` (`string`)
  - `attn` (`string`)
- `recipient` (`object` *(required)*)
  - `name` (`string` *(required)*)
  - `street` (`string` *(required)*)
  - `zip` (`string` *(required)*)
  - `city` (`string` *(required)*)
  - `country` (`string` *(required)*)
  - `state` (`string`)
  - `attn` (`string`)
- `cover` (`boolean` (default: `true`))
- `fit_approved` (`boolean` (default: `false`))
- `terms` (`boolean` (default: `false`))
- `topic` (`string`)
- `handwritten_ack` (`boolean` (default: `false`))
- `pay` (`boolean` (default: `true`))

### `GET /v1/letters` — List recent letters

Returns up to 100 letters created through the API in the key's mode (`live` or `test`).

### `GET /v1/letters/{id}` — Get a letter

Returns status, price, timestamps and tracking number for one letter.

### `POST /v1/letters/{id}/pay` — Pay an unpaid letter from credit

Pays an existing `awaiting_confirmation` or `awaiting_payment` letter from prepaid credit within the API key's spending limit.

**Request body fields:**

- `terms` (`boolean` *(required)*)

### `POST /v1/letters/{id}/cancel` — Cancel a letter before printing

Cancels an unpaid draft or withdraws a paid letter before it has been printed, returning credit to the balance.

### `GET /v1/letters/{id}/proof` — Get proof certificate facts or PDF

Returns proof hashes, timestamps and tracking JSON, or streams the proof certificate PDF when `?format=pdf` or `Accept: application/pdf` is passed.

## Core Schemas

### `Address`
- `name` (`string` *(required)*)
- `street` (`string` *(required)*)
- `zip` (`string` *(required)*)
- `city` (`string` *(required)*)
- `country` (`string` *(required)*)
- `state` (`string`)
- `attn` (`string`)
