> This documentation belongs to Invofox, an AI-powered document processing
> platform. The API extracts structured data (fields) from unstructured
> documents (invoices, contracts, forms, bank statements, and more).
> Key concepts: Account, Environment, Document Type, Field, File, Import,
> and Document. When answering, prefer the exact terminology defined in the
> Glossary and cite the relevant API Reference endpoint when applicable.

# Integrating Invofox

> Complete developer guide to integrate the Invofox document parsing API — get your API key, upload a document, and retrieve clean structured JSON via webhooks or polling.

**Invofox is one API to extract structured data from PDFs and images** — send any document (invoices, receipts, bank statements, delivery notes, contracts and many more, including custom document types) and get back clean, structured JSON. Under the hood it's an intelligent document processing (IDP) platform: OCR + AI that classify each document and extract its fields automatically — no templates, no manual data entry.

This page is the core integration — the **happy path**, from upload to JSON. For use cases, accuracy and pricing, see [invofox.com →](https://www.invofox.com/en/)

<Tip>
**Using an AI coding agent?** Connect to the Invofox MCP server so your agent can search these docs in real time. [Integrate with an AI agent →](/documentation/integrate-with-an-ai-agent)
</Tip>

## Step 1 — Get your API key

API keys are scoped to an environment. To create one:

1. Log in to [app.invofox.com](https://app.invofox.com) and open your environment — [create a free account](https://app.invofox.com/signup) if you don't have one yet.
2. Click **API and Webhooks** in the left menu.
3. Create a new API key and copy it.

## Step 2 — Upload a document

Send your file as `multipart/form-data` with an `info` JSON field. The response returns a `documentId` — that's all you need. [Full API reference →](https://developers.invofox.com/api-reference/ingest-reference/files/upload-direct-controller-upload-direct)

<div className="ivf-endpoint"><span className="ivf-method ivf-method-post">POST</span><span>https://api.invofox.com/v1/ingest/uploads</span></div>

<CodeBlocks>

**`cURL`**

```bash title="cURL"
curl -s -X POST "https://api.invofox.com/v1/ingest/uploads" \
  -H "x-api-key: $INVOFOX_API_KEY" \
  -F "files=@invoice.pdf" \
  -F 'info={"type":"<TYPE_ID>","clientData":{"internalId":"inv-1234"}}'
```

**`Python`**

```python title="Python"
import os, json, requests

headers = {"x-api-key": os.environ["INVOFOX_API_KEY"]}

with open("invoice.pdf", "rb") as f:
    resp = requests.post(
        "https://api.invofox.com/v1/ingest/uploads",
        headers=headers,
        files=[("files", ("invoice.pdf", f, "application/pdf"))],
        data={"info": json.dumps({"type": "<TYPE_ID>", "clientData": {"internalId": "inv-1234"}})}
    ).json()

document_id = resp["files"][0]["documentId"]
print("Document ID:", document_id)
```

**`Node.js`**

```javascript title="Node.js"
import fs from 'fs';
import FormData from 'form-data';
import fetch from 'node-fetch';

const headers = { 'x-api-key': process.env.INVOFOX_API_KEY };

const form = new FormData();
form.append('files', fs.createReadStream('invoice.pdf'), 'invoice.pdf');
form.append('info', JSON.stringify({ type: '<TYPE_ID>', clientData: { internalId: 'inv-1234' } }));

const resp = await fetch('https://api.invofox.com/v1/ingest/uploads', {
  method: 'POST',
  headers: { ...headers, ...form.getHeaders() },
  body: form
}).then(r => r.json());

const documentId = resp.files[0].documentId;
console.log('Document ID:', documentId);
```
</CodeBlocks>

### The `info` field

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `type` | string | <span className="ivf-badge ivf-badge-blue">Required</span> | The Document Type to extract — its `id` or `alias`. Find it in **Invofox → Document Types**. \* |
| `clientData` | object | <span className="ivf-badge ivf-badge-gray">Optional</span> | Any JSON object you want Invofox to store and return alongside the document — in both the GET response and webhook payload. Useful for carrying your own identifiers: `{"internalId": "inv-1234"}`. |

<Info title="Where to find Document Type IDs">
- Open **Document Types** in the left menu of Invofox.
- Each Document Type shows a card with its **`id`** and **`alias`**.
- Use the **`alias`** if it has one; otherwise use the **`id`**.
- **Need a Document Type that isn't there?** Invofox can define custom Document Types for your use case — contact [support@invofox.com](mailto:support@invofox.com).
</Info>

### Upload response

The upload returns immediately with a `documentId` — not the extracted data. Invofox processes the document asynchronously. Save the `documentId` from `files[0]` and use it in Step 3 to retrieve the results.

**`Upload response`**

```json title="Upload response"
{
  "accountId": "666839c78ea472012a9b8875",
  "environmentId": "6a338e5239207f9311eccd36",
  "importId": "6a34a304f55a9cdc5e6d1c76",
  "files": [
    {
      "id": "6a34a304f55a9cdc5e6d1c79",
      "filename": "invoice.pdf",
      "documentId": "6a34a304f55a9cdc5e6d1c7a"   // ← save this
    }
  ]
}
```

## Step 3 — Get the extracted data

Processing is asynchronous. **Webhooks are the recommended approach**; polling is fine for quick tests and prototyping.

<Cards>
  <Card title="Option A · Webhooks (Recommended)" icon="bolt">
    Invofox pushes a `document.processed` event to your server when extraction is complete. Event-driven, no polling overhead. Recommended for all production integrations.
  </Card>
  <Card title="Option B · Polling" icon="arrows-rotate">
    No infrastructure needed — great for prototyping and quick tests. Not recommended for production.
  </Card>
</Cards>

<Info>
Whichever method you use, the **set of fields is defined by the Document Type configured in your environment — it is not fixed**. The **structure** is always the same: every field is a `{ "value": ... }` object, which can be nested, with arrays for line items. To see the exact fields for your Document Type, check **Document Types** in the dashboard or process a sample document.
</Info>

### Option A · Webhooks (recommended for production)

In Invofox, open your environment and go to **API and Webhooks → Webhooks** to add your endpoint URL. Your server will then receive a `document.processed` event the moment extraction finishes.

For the **complete list of events and their payload schemas** (plus signing and verification), see the [Webhooks Reference →](https://developers.invofox.com/webhooks-reference/webhooks-reference/document-processing/receive-document-created). The example below shows a `document.processed` payload.

**`document.processed payload`**

```json title="document.processed payload"
{
  "type": "document.processed",
  "version": "1.2",
  "timestamp": 1781834535319,
  "url": "",
  "data": {                                // ← the document lives here (not under "result" like the GET); no permission flags
    "_id": "6a34a304f55a9cdc5e6d1c7a",
    "clientData": { "internalId": "inv-1234" },   // ← same clientData you sent on upload
    "type": "6a338ee0fdcde8d97ac96784",           // ← the Document Type's id (or its alias, if it has one)
    "name": "invoice.pdf",
    "publicState": "approved",
    "confidence": "high",
    "pageCount": 1,
    "images": [
      "https://storage.invofox.com/documents/6a34a304f55a9cdc5e6d1c7a/page-1.jpeg?token=..."
    ],
    "original": "https://storage.invofox.com/documents/6a34a304f55a9cdc5e6d1c7a/original.pdf?token=...",
    "data": {                              // ← extracted fields at data.data.<field>
      "receiptNumber":  { "value": "A16030202" },
      "issueDate":      { "value": "2023-12-04" },
      "subtotalAmount": { "value": 31.32 },
      "taxAmount":      { "value": 0 },
      "totalAmount":    { "value": 31.32 },
      "paymentMethod":  { "value": "Transferencia bancaria" },
      "merchant":       { "name": { "value": "Invofox Inc" } },
      "items": [
        { "description": { "value": "Permanente Sharpie" }, "quantity": { "value": 1 }, "unitPrice": { "value": 2.1 }, "totalAmount": { "value": 2.1 } }
      ]
    }
  }
}
```

<Warning>
A file can also fail **before producing any document** (password-protected, corrupt, unsupported format). Listen for the `file.rejected` webhook so you don't wait forever for a document that never arrives — this applies whether you use webhooks or polling. See [Rejected files](/documentation/rejected-files).
</Warning>

### Option B · Polling

Poll `GET /documents/{documentId}` using exponential backoff (start at 5 s, double each retry). The endpoint may return 404 for the first several seconds — that's expected, keep polling. Stop when you get a 200 with `publicState` set to `approved`, `pendingCorrection`, or `discarded`.

<div className="ivf-endpoint"><span className="ivf-method ivf-method-get">GET</span><span>https://api.invofox.com/documents/{documentId}</span></div>

Final response fields live under `result.data`. The example below is illustrative.

**`GET /documents/{documentId}`**

```json title="GET /documents/{documentId}"
{
  "canEdit": true, "canComment": false, "canLock": true, "canAction": true,   // ← permission flags (not in the webhook)
  "result": {
    "_id": "6a34a304f55a9cdc5e6d1c7a",
    "clientData": { "internalId": "inv-1234" },   // ← your clientData, returned untouched
    "type": "6a338ee0fdcde8d97ac96784",           // ← the Document Type's id (or its alias, if it has one)
    "name": "invoice.pdf",
    "publicState": "approved",
    "confidence": "high",
    "pageCount": 1,
    "images": [
      "https://storage.invofox.com/documents/6a34a304f55a9cdc5e6d1c7a/page-1.jpeg?token=..."
    ],
    "original": "https://storage.invofox.com/documents/6a34a304f55a9cdc5e6d1c7a/original.pdf?token=...",
    "data": {                              // ← extracted fields at result.data.<field>
      "receiptNumber":  { "value": "A16030202" },
      "issueDate":      { "value": "2023-12-04" },
      "subtotalAmount": { "value": 31.32 },
      "taxAmount":      { "value": 0 },
      "totalAmount":    { "value": 31.32 },
      "paymentMethod":  { "value": "Transferencia bancaria" },
      "merchant":       { "name": { "value": "Invofox Inc" } },
      "items": [
        { "description": { "value": "Permanente Sharpie" }, "quantity": { "value": 1 }, "unitPrice": { "value": 2.1 }, "totalAmount": { "value": 2.1 } }
      ]
    }
  }
}
```

## Further optional steps

### Give feedback on extracted values

If an extracted value is missing or incorrect, you can send the right value back via `PUT /documents/{id}`. Every correction feeds directly into model training, improving extraction accuracy over time — automatically, with no extra configuration. See the [Feedback loop](/documentation/feedback-loop) page for the full reference.

## Additional Features

The core integration is complete. These features are available if your use case requires them:

<Cards>
  <Card title="Feedback loop" icon="pen" href="/documentation/feedback-loop">
    Feed human corrections back into the API to close the human review loop.
  </Card>
  <Card title="Splitter" icon="scissors" href="/documentation/splitter">
    Upload a PDF that contains several documents and get each one back separately, automatically.
  </Card>
  <Card title="Classifier" icon="tag" href="/documentation/classifier">
    Skip specifying the document type — Invofox detects it automatically.
  </Card>
</Cards>