# PDF Report API

The PDF Report API allows API clients to asynchronously generate a downloadable PDF report for a connected SQR user.
The flow is fully async: a job is enqueued immediately and processed in the background.
Once the job completes, a time-limited pre-signed download URL is issued on demand.

All three endpoints are authenticated using a **Bearer token** obtained from the [OAuth Token API](/guides/oauth-token).

## How It Works

Generating a PDF report is a three-step asynchronous process:

```
1. POST /v1/document/api        → Enqueue a report job (returns 202 + jobId)
2. GET  /v1/document/api/{id}   → Poll until status is COMPLETE or FAILED
3. GET  /v1/document/api/{id}/url → Retrieve the signed download URL
```

### Step 1 — Create a Report Job

**`POST /v1/document/api`**

Submit a request to generate a PDF report for a specific user.
The API responds immediately with `202 Accepted` and returns a `jobId`.
No report has been generated yet at this point; the job is placed in a processing queue.

**Rate limit:** Max 10 requests per 10 minutes per org.

#### Request

*Headers*

```
Authorization: Bearer <access_token>
Content-Type: application/json
```

*Body*

```json
{
  "userId": "US1A2B3C4D5E6F78"
}
```

| Field | Type | Required | Description |
|  --- | --- | --- | --- |
| `userId` | string | Yes | The SQR user ID (`AppUser.id`) for whom the report is being generated. Must be 1–16 characters. |


#### Response — `202 Accepted`

```json
{
  "jobId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "status": "PENDING",
  "userId": "US1A2B3C4D5E6F78",
  "createdAt": "2025-05-05T10:00:00.000Z",
  "updatedAt": "2025-05-05T10:00:00.000Z"
}
```

Store the `jobId` — you will need it to check job status and retrieve the download URL.

### Step 2 — Poll Job Status

**`GET /v1/document/api/{id}`**

Returns the current status and metadata of the report generation job.
Poll this endpoint until `status` reaches a terminal state: `COMPLETE` or `FAILED`.

#### Request

*Headers*

```
Authorization: Bearer <access_token>
```

*Path parameter*

| Parameter | Type | Description |
|  --- | --- | --- |
| `id` | string | The `jobId` returned in Step 1 |


#### Response — `200 OK`

```json
{
  "jobId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "status": "COMPLETE",
  "userId": "US1A2B3C4D5E6F78",
  "templateVersion": "v2",
  "sizeBytes": 204800,
  "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "durationMs": 1420,
  "createdAt": "2025-05-05T10:00:00.000Z",
  "updatedAt": "2025-05-05T10:00:02.000Z",
  "completedAt": "2025-05-05T10:00:02.000Z"
}
```

#### Job Status Values

| Status | Description |
|  --- | --- |
| `PENDING` | Job is queued and waiting to be processed |
| `IN_PROGRESS` | Job is currently being processed |
| `COMPLETE` | Report generated successfully — download URL available |
| `FAILED` | Report generation failed — see `errorCode` |


#### Polling Recommendation

Poll at a reasonable interval to avoid unnecessary load. A recommended strategy is:

- Poll every **3–5 seconds** for the first 30 seconds.
- If still not complete, back off to every **10–15 seconds**.
- Treat any job that has not completed within **5 minutes** as failed and surface an appropriate error to your user.


### Step 3 — Retrieve the Signed Download URL

**`GET /v1/document/api/{id}/url`**

Once the job status is `COMPLETE`, request a time-limited pre-signed S3 URL to download the PDF report.

- The URL is valid for **5 minutes** from the time it is issued.
- RBAC and org scope are re-validated on every call — URLs are never cached server-side.
- If the report is not yet complete, the API returns `422 Unprocessable Entity`.


#### Request

*Headers*

```
Authorization: Bearer <access_token>
```

*Path parameter*

| Parameter | Type | Description |
|  --- | --- | --- |
| `id` | string | The `jobId` returned in Step 1 |


#### Response — `200 OK`

```json
{
  "url": "https://s3.amazonaws.com/sqr-reports/...?X-Amz-Signature=...",
  "expiresInSeconds": 300,
  "filename": "SQR_J_Smith_ABCD.pdf"
}
```

| Field | Type | Description |
|  --- | --- | --- |
| `url` | string | Pre-signed S3 download URL, valid for 5 minutes |
| `expiresInSeconds` | number | URL TTL in seconds (always `300`) |
| `filename` | string | Suggested filename to use when saving the PDF |


Use the `url` value to initiate a direct download from S3. The URL will expire after `expiresInSeconds` seconds and cannot be re-used.
If you need to allow another download after expiry, simply call this endpoint again to receive a fresh URL.

## Error Reference

| Status Code | Meaning |
|  --- | --- |
| `401` | Missing or invalid Bearer token. Obtain a new token via the [OAuth Token API](/guides/oauth-token). |
| `403` | Forbidden. The authenticated org does not have permission to access this user's report. |
| `404` | The specified `userId` (Step 1) or `jobId` (Steps 2–3) was not found. |
| `409` | A pending job already exists for this user. Wait for it to complete before creating a new one. |
| `422` | The report job is not yet complete (Step 3 only). Continue polling Step 2. |
| `429` | Rate limit exceeded. Maximum 10 requests per 10 minutes per org (Step 1 only). |
| `5XX` | Internal server error. Contact [support@sqr.id](mailto:support@sqr.id). |


## Full Example Flow

```http
# Step 1 — Create the job
POST /v1/document/api
Host: api.sqr-group.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

{
  "userId": "US1A2B3C4D5E6F78"
}

# → 202 Accepted
# {
#   "jobId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
#   "status": "PENDING",
#   ...
# }

---

# Step 2 — Poll until COMPLETE
GET /v1/document/api/3fa85f64-5717-4562-b3fc-2c963f66afa6
Host: api.sqr-group.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

# → 200 OK
# {
#   "jobId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
#   "status": "COMPLETE",
#   ...
# }

---

# Step 3 — Get the download URL
GET /v1/document/api/3fa85f64-5717-4562-b3fc-2c963f66afa6/url
Host: api.sqr-group.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

# → 200 OK
# {
#   "url": "https://s3.amazonaws.com/...",
#   "expiresInSeconds": 300,
#   "filename": "SQR_J_Smith_ABCD.pdf"
# }
```