> For a complete page index of the Captain API documentation, fetch https://docs.captain.dev/llms.txt?excludeSpec=true

# Mint Asset URLs

POST https://api.captain.dev/v3/collections/{collection_name}/documents/{document_id}/asset-urls
Content-Type: application/json

Mint short-lived presigned URLs for a document's figure crops.

Supply the figure ids (`regions[].figure_id`) to mint URLs for. When batch
requested, each item degrades on its own: a figure with no crop does not fail
the whole request.

Reference: https://docs.captain.dev/reference/v3/document/asset-urls

## Request

### Path parameters

- `collection_name` (string, required)
- `document_id` (string, required)

### Body (application/json)

This endpoint expects an AssetUrlsRequestV3.

- `asset_ids` (list of string, required) — Figure ids to mint URLs for, from `regions[].figure_id`. Stable and safe to store, unlike the URLs they mint. Up to 100; duplicates each return their own item.
- `ttl_seconds` (integer, optional, nullable) — How long each URL stays valid, in seconds. Default 900 (15 min); min 60, max 604800 (7 days).

## Response

### 200

Successful Response

- `collection_name` (string, required)
- `document_id` (string, required)
- `asset_urls` (list of AssetUrlV3, optional)
- `expires_at` (string, optional, nullable) — Earliest expiry across all signed items, so a client can schedule a single refresh instead of one timer per image. Null when nothing was signed.
- `expires_in_seconds` (integer, optional, nullable) — Same deadline as a duration. Provided because browser clocks are routinely wrong, which makes an absolute-only expiry unreliable.
- `signed_count` (integer, optional, default: 0)
- `unavailable_count` (integer, optional, default: 0)
- `warnings` (list of string, optional)

## Errors

### 403 Forbidden Error

The API key does not have query permission on this collection.

- `error` (string, required) — Error kind. `HTTP_ERROR` for errors raised by the route.
- `message` (string, required) — What went wrong.
- `path` (string, required) — The request path.
- `status_code` (integer, optional) — The HTTP status code.

### 404 Not Found Error

Collection or document not found.

- `message` (string, required) — What went wrong.
- `path` (string, required) — The request path.
- `code` (string, optional) — `document_not_found` when the document is missing. Absent on `HTTP_ERROR`.
- `error` (string, optional) — `HTTP_ERROR` when the collection is missing. Absent on `document_not_found`.
- `status_code` (integer, optional) — Present only on `HTTP_ERROR`.

### 422 Unprocessable Entity Error

asset_ids missing, empty, over the batch limit, or malformed; or ttl_seconds out of range.

- `error` (string, required) — `VALIDATION_ERROR`.
- `message` (string, required)
- `details` (list of V3CollectionsCollectionNameDocumentsDocumentIdAssetUrlsPostResponsesContentApplicationJsonSchemaDetailsItems, required) — One entry per invalid field, with `type`, `loc` and `msg`.
- `path` (string, required)

## Types

### AssetUrlV3

- `asset_id` (string, required) — Echoes the requested id, so a response can be keyed or positionally indexed. For figures this is `regions[].figure_id`.
- `status` (enum, required)
  - Allowed values: `signed`, `unavailable`
- `bbox` (map from string to double, optional, nullable)
- `content_type` (string, optional, nullable)
- `expires_at` (string, optional, nullable) — RFC 3339 UTC instant after which `image_url` stops working.
- `image_url` (string, optional, nullable)
- `kind` (string, optional, default: figure) — The kind of asset this id addresses, from its prefix. Currently always `figure`. Treat unknown values as opaque.
- `message` (string, optional, nullable)
- `page` (integer, optional, nullable) — The figure's page in the source document, 1-based. On a document split for parallel parsing this can differ from `regions[].page` (the page within the split as the parser saw it). This field is the one that matches the image.
- `reason` (string, optional, nullable) — Set when status is `unavailable`. Open-ended: treat unknown values as generic failures. Current values: `no_crop_available` (document indexed before figure cropping; re-index to generate crops), `figure_not_found` (no figure in this document has that id), `ocr_unavailable` (layout data could not be read; retry, do not discard the id), `signing_unavailable` (crop signing not configured on this deployment).

### V3CollectionsCollectionNameDocumentsDocumentIdAssetUrlsPostResponsesContentApplicationJsonSchemaDetailsItems

## Examples

**Request**

```json
{
  "asset_ids": [
    "fig_1_a2b3c4d5e6f7g2h3i4j5k6l7m2",
    "fig_1_z7y6x5w4v3u2t7s6r5q4p3o2n7"
  ],
  "ttl_seconds": 1800
}
```

**Response**

```json
{
  "collection_name": "research_library",
  "document_id": "doc_climate_agriculture_2024",
  "asset_urls": [
    {
      "asset_id": "fig_1_a2b3c4d5e6f7g2h3i4j5k6l7m2",
      "status": "signed",
      "bbox": {
        "top": 0.12,
        "left": 0.2,
        "width": 0.5,
        "height": 0.31
      },
      "content_type": "image/png",
      "expires_at": "2024-05-21T08:30:00Z",
      "image_url": "https://assets.captainusercontent.com/crops/org_1/doc_climate_agriculture_2024/p4-2.png?X-Amz-Expires=1800&X-Amz-Signature=REDACTED",
      "kind": "figure",
      "page": 4
    },
    {
      "asset_id": "fig_1_z7y6x5w4v3u2t7s6r5q4p3o2n7",
      "status": "unavailable",
      "bbox": {
        "top": 0.4,
        "left": 0.15,
        "width": 0.6,
        "height": 0.22
      },
      "kind": "figure",
      "message": "This document was indexed before figure cropping shipped, so no crop image exists. Re-index the document to generate crops.",
      "page": 7,
      "reason": "no_crop_available"
    }
  ],
  "expires_at": "2024-05-21T08:30:00Z",
  "expires_in_seconds": 1800,
  "signed_count": 1,
  "unavailable_count": 1,
  "warnings": []
}
```

**SDK Code**

```python
import json
import requests

BASE_URL = "https://api.captain.dev"
API_KEY = "your_api_key"
COLLECTION_NAME = "research_library"
DOCUMENT_ID = "doc_climate_agriculture_2024"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

response = requests.post(
    f"{BASE_URL}/v3/collections/{COLLECTION_NAME}/documents/{DOCUMENT_ID}/asset-urls",
    headers=headers,
    json={
        "asset_ids": [
            "fig_1_a2b3c4d5e6f7g2h3i4j5k6l7m2",
            "fig_1_z7y6x5w4v3u2t7s6r5q4p3o2n7",
        ],
        "ttl_seconds": 1800,
    },
    timeout=30.0,
)

print(json.dumps(response.json(), indent=2))
```

```typescript
const BASE_URL = "https://api.captain.dev";
const API_KEY = "your_api_key";
const COLLECTION_NAME = "research_library";
const DOCUMENT_ID = "doc_climate_agriculture_2024";

const headers = {
  Authorization: `Bearer ${API_KEY}`,
  "Content-Type": "application/json",
};

const response = await fetch(
  `${BASE_URL}/v3/collections/${COLLECTION_NAME}/documents/${DOCUMENT_ID}/asset-urls`,
  {
    method: "POST",
    headers,
    body: JSON.stringify({
      asset_ids: ["fig_1_a2b3c4d5e6f7g2h3i4j5k6l7m2", "fig_1_z7y6x5w4v3u2t7s6r5q4p3o2n7"],
      ttl_seconds: 1800,
    }),
  },
);

console.log(JSON.stringify(await response.json(), null, 2));
```