> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gigstack.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Documents API Guide

> Integration guide for Documents

<Warning>Some operations in this guide have Discovery handlers but no public gateway route yet: `POST /invoices/{id}/support-documents`, `GET /documents`, `POST /documents`, `GET /documents/{id}`, `PATCH /documents/{id}`, `DELETE /documents/{id}`, `POST /documents/{id}/analyze`, `DELETE /documents/{id}/link`. Check the availability notice on each API reference page before using them.</Warning>

## Overview

The Documents API stores the metadata of supporting documents — contracts, proof of delivery, proof of payment, communications — and links them to the invoices, payments, receipts and clients they support. The SAT can ask for this evidence to validate an operation (*materialidad*); keeping it in gigstack, linked to the CFDI, means you can produce it when asked.

Base path: `https://api.gigstack.io/v2/documents`

Documents belong to the team and mode (`livemode`) of the API key that created them.

> **Uploading a file for a specific invoice, payment or client?** The `POST /invoices/{id}/support-documents` (and the equivalent client and payment endpoints) accept the file itself and link it in one call — see [Invoices](/guides/invoices#support-documents). The Documents API below records documents whose file is **already in storage**, and manages all of them in one place.

## Key Features

* **Link one document to many entities** - invoices, payments, receipts, clients
* **Compliance review state** - `pending_review`, `valid`, `requires_update`, `expired`, `rejected`
* **Validity window** - `validFrom` / `validUntil` for contracts and other time-bound evidence
* **AI extraction** - pull structured data out of a PDF or image
* **Soft delete** - deleted documents disappear from the API but are not destroyed

## Endpoints

### List Documents

```http theme={null}
GET /documents
```

| Parameter | Type | Description |
| - | - | - |
| `limit` | integer | Page size (default **50**, max 100) |
| `cursor` | string | Id of the last document from the previous page |
| `document_type` | string | `contract`, `delivery_proof`, `payment_proof` or `communication` |
| `compliance_status` | string | `pending_review`, `valid`, `requires_update`, `expired` or `rejected` |
| `entity_type` | string | Only documents linked to this kind of entity: `invoice`, `payment`, `receipt`, `client` |
| `entity_id` | string | Only documents linked to this entity id (see note) |

Newest first; soft-deleted documents are excluded.

Note the nested shape and the pagination names, which differ from other list endpoints: the array is at `data.data`, and the cursor for the next page is `data.next_cursor` (pass it back as `cursor`).

```bash theme={null}
curl "https://api.gigstack.io/v2/documents?compliance_status=pending_review&limit=20" \
  -H "Authorization: Bearer YOUR_TOKEN"
```

```json theme={null}
{
    "success": true,
    "data": {
        "data": [
            {
                "id": "doc_1234567890",
                "document_type": "contract",
                "name": "Contrato de servicios 2026 — Cliente ACME",
                "compliance_status": "pending_review",
                "linked_entities": [{ "entity_type": "client", "entity_id": "client_1234567890", "linked_at": 1767225600000 }],
                "created_at": 1767225600000
            }
        ],
        "has_more": false,
        "next_cursor": null
    },
    "timestamp": 1767225600000
}
```

`entity_id` is applied after the page is fetched, so it only narrows that page. If matches are sparse, use a larger `limit`.

### Create Document

```http theme={null}
POST /documents
```

Records a document whose file you have **already uploaded** to storage. This endpoint does not accept the file.

| Field | Type | Required | Description |
| - | - | - | - |
| `documentType` | string | yes | `contract`, `delivery_proof`, `payment_proof` or `communication` |
| `name` | string | yes | Display name |
| `fileUrl` | string | yes | URL of the uploaded file |
| `storagePath` | string | yes | Storage path of the uploaded file |
| `fileName` | string | yes | Original file name |
| `description` | string | no | |
| `fileSize` | number | no | Bytes |
| `mimeType` | string | no | e.g. `application/pdf` |
| `linkedEntities` | array | no | `[{ "entityType": "client", "entityId": "client_…" }]` |
| `validFrom` | integer | no | Validity start, epoch ms |
| `validUntil` | integer | no | Validity end, epoch ms |
| `tags` | array | no | Strings |
| `metadata` | object | no | Your own key-value data |
| `analyzeWithAI` | boolean | no | Run AI extraction after creating |

Request fields are **camelCase**; response fields are snake\_case — including the link objects, which you send as `{ "entityType", "entityId" }` and read back as `{ "entity_type", "entity_id", "linked_at" }`. Unknown top-level keys are rejected with `400`. `complianceStatus` cannot be set here — it always starts as `pending_review`.

```bash theme={null}
curl -X POST https://api.gigstack.io/v2/documents \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "documentType": "contract",
    "name": "Contrato de servicios 2026 — Cliente ACME",
    "fileUrl": "https://storage.googleapis.com/gigstack-docs/team_123/contrato-acme.pdf",
    "storagePath": "teams/team_123/documents/contrato-acme.pdf",
    "fileName": "contrato-acme.pdf",
    "mimeType": "application/pdf",
    "validFrom": 1767225600000,
    "validUntil": 1798761600000,
    "linkedEntities": [{ "entityType": "client", "entityId": "client_1234567890" }]
  }'
```

Answers `201` with the document in `data`.

The `file_url` you read back is not necessarily the one you sent: gigstack re-mints it as a Firebase download-token URL for the same object when it answers, so links no longer depend on a public storage ACL. It does not expire — but treat it as opaque and re-read the document rather than constructing or caching the link.

### Get Document

```http theme={null}
GET /documents/{id}
```

A document of another team, or a soft-deleted one, answers `404`.

### Update Document

```http theme={null}
PATCH /documents/{id}
```

Updates metadata and the compliance review. Only the fields you send are written. Accepted fields: `name`, `description`, `complianceStatus`, `complianceNotes`, `validFrom`, `validUntil`, `tags`, `metadata`. The file itself (`fileUrl`, `storagePath`, `fileName`, `documentType`) cannot be changed; sending those keys returns `400`.

```bash theme={null}
curl -X PATCH https://api.gigstack.io/v2/documents/doc_1234567890 \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "complianceStatus": "valid",
    "complianceNotes": "Revisado por el área fiscal, cumple con el requisito de materialidad."
  }'
```

### Link and Unlink

```http theme={null}
POST   /documents/{id}/link
DELETE /documents/{id}/link
```

Both take the same body:

```json theme={null}
{ "entityType": "invoice", "entityId": "B0C3E4F2-1A2B-4C5D-9E8F-0123456789AB" }
```

`entityType` is `invoice`, `payment`, `receipt` or `client`. Linking twice to the same entity returns `400`; linking to an entity that does not exist or belongs to another team returns `404`. Unlinking a document that is not linked returns `400`; if the entity no longer exists the link is still removed. Note that this `DELETE` carries a request body.

### Analyze with AI

```http theme={null}
POST /documents/{id}/analyze
```

Runs AI extraction and stores the result on the document under `ai_extraction`. Only PDFs and PNG/JPEG/WEBP images can be analyzed; a scanned PDF with no text layer returns `400`. The request body is ignored.

### Delete Document

```http theme={null}
DELETE /documents/{id}
```

Soft delete: the document stops appearing in list and get responses and is unlinked from every entity. A second delete returns `404`.

## Errors

All errors use the standardized envelope (`success: false`, `error.code`, `error.message`, `timestamp`).

| Status | When |
| - | - |
| `400` | Body validation failure (including unknown keys), duplicate link, file not analyzable |
| `404` | Document not found, deleted, or of another team; link target not found |
| `500` | Unexpected failure |

## Related Resources

* [Invoices API](/guides/invoices#support-documents) - Upload a file for an invoice in one call
* [Clients API](/guides/clients) - Client support documents
* [Payments API](/guides/payments) - Payment support documents


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.