# Read a filled form and the submission package

Read the labelled values out of a filled PDF, attach the documents a form asks for, and have a commit hand over the filled form, its attachments, and an embedded receipt as one package.

## Read a filled form back

```
POST /forms/read
```

Authentication: Bearer API key required.

Read every value out of a filled PDF, labelled when the form is recognised. Synchronous; nothing is stored.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| file | body | file (multipart) | yes | The filled PDF to read. |
| form_id | body | string | no | Optional. Read against this form's own contract instead of relying on fingerprint recognition. |

```curl
curl -X POST https://api.getemboss.ai/forms/read \
  -H "Authorization: Bearer sk_..." \
  -F "file=@./w9-filled.pdf"
```

```js
const body = new FormData();
body.append("file", filledBlob, "w9-filled.pdf");

const res = await fetch("https://api.getemboss.ai/forms/read", {
  method: "POST",
  headers: { Authorization: "Bearer sk_..." },
  body,
});
const result = await res.json();
```

```python
import requests

res = requests.post(
    "https://api.getemboss.ai/forms/read",
    headers={"Authorization": "Bearer sk_..."},
    files={"file": open("w9-filled.pdf", "rb")},
)
result = res.json()
```

### Response

```json
{
  "recognised": "own_layout",
  "form_id": "6c47f7f5-f921-4698-910f-95dd7d81310b",
  "fingerprint": "fillable:3a9f2c8e1d47b6a0",
  "file_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b85",
  "page_count": 1,
  "fields": [
    { "name": "business_name", "page": 1, "type": "text", "value": "Copperline Hospitality Group, LLC", "filled": true, "id": 1, "label": "Business name", "required": true }
  ],
  "unmatched": [],
  "counts": { "fields": 24, "filled": 24, "required": 15, "required_filled": 15, "signatures_missing": 0 },
  "complete": true
}
```

### Status codes

| Code | Meaning |
| --- | --- |
| 200 | The read result. |
| 400 | Not a PDF, or the file has no form boxes: no form boxes found; scanned or flattened forms are not read yet. |
| 401 | Missing or invalid API key. |
| 402 | No payment method on file and either the monthly free allowance is used up or the request is over the free tier's 5-page cap. The body's detail carries code (over_free_tier or over_page_cap), message and billing_url, the page where a payment method can be added. |
| 404 | form_id given but not owned by this key. |
| 409 | form_id given but that form's detection is not ready. |
| 413 | Upload too large (max 10 MB) or too many pages (max 100). |
| 422 | pdf_unsafe: the PDF's form fields are built in a way that could compromise the service, so it is refused before any price, charge or stored file. The body's detail carries code and message ("Emboss does not process PDF files that could compromise the service."). |
| 429 | Rate limit exceeded. |

## Attach a document to a proposal

```
POST /proposals/{proposal_id}/attachments
```

Authentication: Bearer API key required.

Upload a document to satisfy one of the form's attachment requirements, or an extra document with no requirement named.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| proposal_id | path | string | yes | The proposal to attach to. Must still be in status proposed. |
| file | body | file (multipart) | yes | The attachment: a PDF, PNG or JPEG, sniffed from the bytes. |
| for | body | string | no | Optional. Must equal the name of one of the contract's attachments_required entries. Uploading the same for again replaces the earlier file. |

```curl
curl -X POST https://api.getemboss.ai/proposals/3e714663-ff2c-45ac-ae11-3c7015facfef/attachments \
  -H "Authorization: Bearer sk_..." \
  -F "file=@./passport.pdf" \
  -F "for=Proof of identity"
```

```js
const body = new FormData();
body.append("file", passportBlob, "passport.pdf");
body.append("for", "Proof of identity");

const res = await fetch("https://api.getemboss.ai/proposals/3e714663-ff2c-45ac-ae11-3c7015facfef/attachments", {
  method: "POST",
  headers: { Authorization: "Bearer sk_..." },
  body,
});
const attachment = await res.json();
```

```python
import requests

res = requests.post(
    "https://api.getemboss.ai/proposals/3e714663-ff2c-45ac-ae11-3c7015facfef/attachments",
    headers={"Authorization": "Bearer sk_..."},
    files={"file": open("passport.pdf", "rb")},
    data={"for": "Proof of identity"},
)
attachment = res.json()
```

### Response

```json
{
  "n": 1,
  "for": "Proof of identity",
  "filename": "passport.pdf",
  "bytes": 182334,
  "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b85"
}
```

### Status codes

| Code | Meaning |
| --- | --- |
| 201 | The stored attachment. |
| 400 | unknown requirement: <for>, or attachment must be a PDF, PNG or JPEG, or attachment limit reached (at most 20 attachments and 20 MB in total per proposal). |
| 401 | Missing or invalid API key. |
| 404 | No such proposal, or owned by another key. |
| 409 | The proposal is already committed or has expired. |
| 410 | The form's documents have been deleted under its retention policy. |

## Remove an attachment

```
DELETE /proposals/{proposal_id}/attachments/{n}
```

Authentication: Bearer API key required.

Remove one attachment from a proposal by its number.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| proposal_id | path | string | yes | The proposal the attachment belongs to. |
| n | path | integer | yes | The attachment's number, from the upload response or GET /proposals/{id}. |

```curl
curl -X DELETE https://api.getemboss.ai/proposals/3e714663-ff2c-45ac-ae11-3c7015facfef/attachments/1 \
  -H "Authorization: Bearer sk_..."
```

```js
await fetch("https://api.getemboss.ai/proposals/3e714663-ff2c-45ac-ae11-3c7015facfef/attachments/1", {
  method: "DELETE",
  headers: { Authorization: "Bearer sk_..." },
});
```

```python
import requests

requests.delete(
    "https://api.getemboss.ai/proposals/3e714663-ff2c-45ac-ae11-3c7015facfef/attachments/1",
    headers={"Authorization": "Bearer sk_..."},
)
```

### Status codes

| Code | Meaning |
| --- | --- |
| 204 | Removed. |
| 401 | Missing or invalid API key. |
| 404 | No such proposal or attachment number. |
| 409 | The proposal is already committed or has expired. |
| 410 | The form's documents have been deleted under its retention policy. |

## Read a proposal

```
GET /proposals/{proposal_id}
```

Authentication: Bearer API key required.

Read a proposal: every field's state, the questions still open, the values awaiting confirmation, what has been attached, which attachment requirements are satisfied, and the package link, and for the owner the package's file number, from the newest commit that built one.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| proposal_id | path | string | yes | The proposal to read. |

```curl
curl https://api.getemboss.ai/proposals/3e714663-ff2c-45ac-ae11-3c7015facfef \
  -H "Authorization: Bearer sk_..."
```

```js
const res = await fetch("https://api.getemboss.ai/proposals/3e714663-ff2c-45ac-ae11-3c7015facfef", {
  headers: { Authorization: "Bearer sk_..." },
});
const proposal = await res.json();
```

```python
import requests

res = requests.get(
    "https://api.getemboss.ai/proposals/3e714663-ff2c-45ac-ae11-3c7015facfef",
    headers={"Authorization": "Bearer sk_..."},
)
proposal = res.json()
```

### Response

```json
{
  "proposal_id": "3e714663-ff2c-45ac-ae11-3c7015facfef",
  "form_id": "6c47f7f5-f921-4698-910f-95dd7d81310b",
  "status": "proposed",
  "policy": "safe",
  "attachments": [
    { "n": 1, "for": "Proof of identity", "filename": "passport.pdf", "bytes": 182334, "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b85" }
  ],
  "requirements": {
    "signatures": [],
    "attachments": [{ "name": "Proof of identity", "source": "page 2", "satisfied": true }]
  },
  "package_url": "https://getemboss.ai/d/<token>",
  "artifacts": [
    { "artifact_id": "5b0a9c1e-7d2f-4c43-9a51-0e8c3d6f2b17", "role": "package", "mime_type": "application/pdf" }
  ]
}

// artifacts is how an owner gets the package by file number: the newest finished commit's package,
// when it built one, as { artifact_id, role: "package", mime_type }. It is empty when there is no
// package, and a payer on the pay-per-call API sees the link alone. The number can be locked,
// faxed or fetched like any other artifact.
//
// package_url is the newest finished commit's package, once that commit built
// one, as a signed download link on the web host: https://getemboss.ai/d/<token>.
// It is how a payer on the pay-per-call API collects a package they paid for. That payer signs
// this read, attach and commit with the proposal token, as ?token= or the X-Proposal-Token
// header, instead of an API key; an owner can
// also read the same link off the commit job's status.
```

### Status codes

| Code | Meaning |
| --- | --- |
| 200 | The proposal. |
| 401 | Missing or invalid API key. |
| 404 | No such proposal, or owned by another key. |
| 409 | The proposal is still being prepared: proposal not ready. |

## Commit a proposal, optionally as a package

```
POST /proposals/{proposal_id}/commit
```

Authentication: Bearer API key required.

Write the proposal's values into the PDF, render, verify, and build a receipt. Set package to true to also assemble the filled form, its attachments and an embedded receipt into one package PDF. A commit is free unless package is true: then an account is billed one package, at the package price on the pricing page, and the first 5 each month are free.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| proposal_id | path | string | yes | The proposal to commit. |
| confirm | body | integer[] | no | Field ids whose proposed value is accepted as-is. |
| values | body | array | no | Field values to set for fields the proposal left open: [{ field_id, value, note? }]. |
| policy | body | string | no | Optional. safe or strict, overriding the proposal's own policy for this commit. |
| package | body | boolean | no | Optional, default false. When true, builds a submission package (the filled form, its attachments in requirement order, and an embedded receipt) alongside the ordinary commit. See the submission package guide. |
| idempotency_key | body | string | no | Optional. Retrying a commit with the same key returns the original job instead of committing again. |

```curl
curl -X POST https://api.getemboss.ai/proposals/3e714663-ff2c-45ac-ae11-3c7015facfef/commit \
  -H "Authorization: Bearer sk_..." \
  -H "Content-Type: application/json" \
  -d '{"package": true}'
```

```js
const res = await fetch("https://api.getemboss.ai/proposals/3e714663-ff2c-45ac-ae11-3c7015facfef/commit", {
  method: "POST",
  headers: { Authorization: "Bearer sk_...", "Content-Type": "application/json" },
  body: JSON.stringify({ package: true }),
});
const { job_id, status } = await res.json();
```

```python
import requests

res = requests.post(
    "https://api.getemboss.ai/proposals/3e714663-ff2c-45ac-ae11-3c7015facfef/commit",
    headers={"Authorization": "Bearer sk_..."},
    json={"package": True},
)
job = res.json()
```

### Response

```json
{
  "job_id": "8c9d0e1f-2a3b-4c5d-9e6f-a1b2c3d4e5f6",
  "status": "processing"
}

// Poll GET /forms/with-context/{job_id} for the finished commit. When package
// was true, the job status carries package_url once the package is built
// (null until then). package_url is a signed download link on the web host,
// not an API route, and it expires 24 hours after it is minted; receipt_url
// and pdf_url are API routes on this host:
{
  "status": "ready",
  "result": "complete",
  "receipt_url": "https://api.getemboss.ai/proposals/3e714663-ff2c-45ac-ae11-3c7015facfef/receipt",
  "pdf_url": "https://api.getemboss.ai/proposals/3e714663-ff2c-45ac-ae11-3c7015facfef/pdf",
  "package_url": "https://getemboss.ai/d/<token>"
}
```

### Status codes

| Code | Meaning |
| --- | --- |
| 202 | Accepted; commit job started. |
| 400 | A multipart body (documents go to prepare, not commit), an unknown field_id, an invalid value, or package was not quoted; prepare again with package=true (pay-per-call API callers only). |
| 401 | Missing or invalid API key. |
| 402 | package is true, no payment method is on file, and this month's free packages are used up. The body's detail carries code, message and billing_url. |
| 404 | No such proposal, or owned by another key. |
| 409 | The proposal is already committed 10 times, or has expired. |
| 410 | The form's documents have been deleted under its retention policy. |

## See also

- [Read a filled form back](https://getemboss.ai/docs/read-filled-form)
- [Fill PDFs from your own data](https://getemboss.ai/use-cases/fill-pdfs-from-your-data)
