# Quickstart: first envelope with two signers

In five steps you send a PDF to two people for signing, learn by webhook when both have finished, and download the signed document. All examples use a **test key**: nothing is charged in test mode, and with the addresses from the guide "Sandbox scenarios" the recipients even sign on their own.

This guide is the English version of the German guide `quickstart` and covers the same steps.

Basics that apply to every route:

- Base URL: your API base URL (`$BASE_URL` in the examples), key as `Authorization: Bearer $KEY`.
- Responses are the object itself (with `object`, for example `envelope`), lists are `{ data, hasMore, nextCursor }`, errors are `{ error: { code, type, message, details, requestId } }`.
- Writing calls accept an `Idempotency-Key`: if you repeat a call with the same key and the same body, you get the stored response instead of a second envelope.
- Client timeout: at least 30 seconds. `POST /v1/signatures` usually answers within 25 seconds (counted from receipt; preparing a large document counts towards them and is never interrupted), finished (200) or with 202 `status: WAIT_SIGN` and `Retry-After`; the result then arrives by webhook or `GET /v1/signatures/{id}` (German guide `qes-members`, section on response time).

## 1. Check the key

In the dashboard, under **Developers → API keys**, create a test key (`qgw_test_…`). For this guide it needs the scopes `envelopes:read`, `envelopes:write` and `webhooks:manage`.

```sh
curl "$BASE_URL/v1/me" -H "Authorization: Bearer $KEY"
```

`GET /v1/me` shows the organization, the mode (`TEST`), the scopes and the rate limits of the key.

## 2. Create a webhook subscription

So that you do not have to poll, subscribe to the events of envelopes:

```sh
curl -X POST "$BASE_URL/v1/webhook-subscriptions" \
  -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: quickstart-subscription-1' \
  -d '{ "url": "https://example.com/hooks/signatures", "events": ["envelope.*", "recipient.*"] }'
```

The response contains `secret` (`whsec_…`) **this one time only**. Store it: it verifies the signature of every delivery (see the guide "Receiving and verifying webhooks"). A test key creates a subscription in test mode, which only receives test events.

## 3. Create and send an envelope

An envelope bundles documents, recipients and fields. With `send: true` it is created and sent in one call:

```sh
curl -X POST "$BASE_URL/v1/envelopes" \
  -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: quickstart-envelope-1' \
  -d '{
    "title": "Rental agreement, 1 Sample Street",
    "externalId": "crm-4711",
    "expiresInDays": 14,
    "signingOrder": "SEQUENTIAL",
    "documents": [{ "name": "Rental-agreement.pdf", "base64": "JVBERi0xLjcK…" }],
    "recipients": [
      { "recipientKey": "tenant", "role": "SIGNER", "firstName": "Anna", "lastName": "Weber",
        "email": "anna.weber@example.com", "signatureLevel": "SES" },
      { "recipientKey": "landlord", "role": "SIGNER", "firstName": "Jonas", "lastName": "Keller",
        "email": "jonas.keller@example.com", "signatureLevel": "SES" }
    ],
    "send": true
  }'
```

- `base64` takes PDFs up to 11 MB (the length of the base64 text). Upload larger files beforehand with `POST /v1/documents` or pass a `fileUrl`; the document then carries `documentId` or `fileUrl` instead of `base64`.
- Without fields, every signer gets a signature field on the last page. Set your own fields with `fields` (page and rectangle in PDF points).
- `signingOrder: SEQUENTIAL` invites the second person only when the first has finished; `PARALLEL` invites both at once.
- `signatureLevel` is the simple electronic signature (`SES`) here. You choose `AES` (advanced) and `QES` (qualified) per recipient; a QES is only possible for members of your organization who hold a certificate (German guide `qes-members`). The own simple signature (signing with a one-time code directly on our signing page) is currently available **in test mode only**.

To see before sending whether everything is in order and what it costs, send the same body to `POST /v1/envelopes?dryRun=true`: the response is a preview (`envelope_send_preview` with `valid`, `errors`, `warnings` and `costPreview`), and nothing is created.

Alternatively, create a draft first (without `send`), complete it with `POST /v1/envelopes/{id}/recipients`, `POST /v1/envelopes/{id}/documents` and `POST /v1/envelopes/{id}/fields`, and then send it with `POST /v1/envelopes/{id}/send`.

## 4. React to events

Your endpoint receives, among others and in this order:

| Event | Meaning |
| --- | --- |
| `envelope.sent` | The envelope was sent. |
| `recipient.notified` | A person is next, the invitation is on its way. |
| `recipient.signed` | A person has signed. |
| `envelope.completed` | Everyone has finished; the final PDF and the audit trail are ready. |

Every event carries the `envelopeId` and your `externalId` in `context`, and in `data` the object as returned by `GET /v1/envelopes/{id}`. Answer quickly with `2xx` and process in the background. You can read the current state at any time with `GET /v1/envelopes/{id}` and the timeline with `GET /v1/envelopes/{id}/events`.

Without a webhook endpoint (for example while trying things out), poll `GET /v1/events`: it lists the events of the last 30 days, newest first.

## 5. Download the result

After `envelope.completed`:

```sh
curl -o Rental-agreement-signed.pdf "$BASE_URL/v1/envelopes/$ENVELOPE_ID/documents/final" \
  -H "Authorization: Bearer $KEY"
curl -o Audit-trail.pdf "$BASE_URL/v1/envelopes/$ENVELOPE_ID/audit-trail" \
  -H "Authorization: Bearer $KEY" -H 'Accept: application/pdf'
```

`GET /v1/envelopes/{id}/documents/final` returns the final PDF (with `?format=base64` as the JSON object `file`), `GET /v1/envelopes/{id}/audit-trail` the audit trail as JSON or, with `Accept: application/pdf`, as a PDF. For systems without an API key, `POST /v1/envelopes/{id}/download-links` creates links that are valid for 15 minutes.

## What next?

- Recurring documents: the German guide `templates` (`/docs/guides/templates.md`).
- Test runs without real people: the guide "Sandbox scenarios" (`/docs/guides/sandbox-en.md`).
- Costs in advance: the German guide `costs` (`/docs/guides/costs.md`).
- All routes, fields and error codes: the API reference at `/docs`.
