The submission package
Once a form's answers are committed, hand the whole submission over in one file: the filled form, every attachment the form asked for in the order it asked for them, and a receipt embedded inside the PDF itself.
Attach a document before you commit
POST /proposals/{proposal_id}/attachments takes a multipart file and an
optional for, naming which of the form's attachments_required entries
this file satisfies.
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"
{ "n": 1, "for": "Proof of identity", "filename": "passport.pdf", "bytes": 182334, "sha256": "e3b0c...b855" }
Rules, all checked at upload so the commit itself never fails because of an attachment:
for, when given, must equal thenameof one of the contract'sattachments_requiredentries. Anything else:400"unknown requirement: <for>".- Only PDF, PNG or JPEG files are accepted, sniffed from the bytes, at the
usual upload size limit. Anything else:
400"attachment must be a PDF, PNG or JPEG". - Up to 20 attachments per proposal, and 20 MB across all of them together.
Either limit:
400"attachment limit reached". - Uploading the same
foragain replaces the earlier file. - Attachments can only be added while the proposal is
proposed. Once it is committed or has expired:409.
DELETE /proposals/{proposal_id}/attachments/{n} removes one by its number.
curl -X DELETE https://api.getemboss.ai/proposals/3e714663-ff2c-45ac-ae11-3c7015facfef/attachments/1 \
-H "Authorization: Bearer sk_..."
GET /proposals/{proposal_id} lists what has been attached so far and which
requirements are already satisfied:
{
"attachments": [{ "n": 1, "for": "Proof of identity", "filename": "passport.pdf", "bytes": 182334, "sha256": "e3b0c...b855" }],
"requirements": { "attachments": [{ "name": "Proof of identity", "source": "page 2", "satisfied": true }] }
}
Build the package at commit
POST /proposals/{proposal_id}/commit takes an optional "package": true in
its JSON body (default false). When true, after the filled PDF renders,
Emboss assembles one package PDF:
- The filled form, all pages.
- The attachments whose
formatches anattachments_requiredentry, in that requirement list's own order. - Any unlabelled attachments, in upload order.
- Each image attachment becomes one page, scaled to fit the form's first page size and centred.
- The receipt is embedded inside the package as a file attachment named
receipt.json, so the package carries its own record without a second download.
A commit with package: true and no attachments still produces a package:
the form plus the embedded receipt, with every requirement listed as
outstanding.
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}'
Once the commit job finishes, its status carries package_url. Unlike the
pdf_url and receipt_url API addresses, which need your key, it is a
download link on the website (https://getemboss.ai/d/<token>) that
needs no key and expires 24 hours after it is made. After that, fetch the
package by its artifact_id (its entry in the job's artifacts has role
package) at GET /artifacts/{artifact_id}/content.
{ "job_id": "8c9d0e1f-2a3b-4c5d-9e6f-a1b2c3d4e5f6", "status": "processing" }
Once the job is ready, package_url sits alongside receipt_url and
pdf_url on the poll response.
The receipt's outstanding list
The receipt's requirements.attachments becomes one entry per requirement:
{ "name": "Proof of identity", "source": "page 2",
"satisfied_by": { "n": 1, "bytes": 182334, "sha256": "e3b0c...b855" } }
satisfied_by is null when nothing was attached for that requirement. The
receipt's unresolved block is {"fields": [...], "attachments": [...]}:
the fields with their candidates, and the attachments still outstanding,
alongside the missing signatures, so an agent reads one list of everything
still standing between the person and a filing, values and attachments
together.
Retention
Attachments and packages are documents, and they follow the same retention
rule as everything else Emboss stores: Ephemeral processing: source and output documents are deleted 60 to 70 minutes after the last activity on a form. Once a form is
swept, its package and attachment routes answer 410 Gone with that same
sentence as the problem detail, exactly like the filled PDF and the fillable
copy. DELETE /forms/{id} removes them along with everything else on the
form. See Security for the full list of what is kept and what
is deleted.
Not on the one-call path
A one-call context fill has no proposal to attach a document to, so package
is not offered on that path: there is nowhere to call the attachments route
before the fill runs. Use prepare and commit
when a submission needs attachments or a package.
On the pay-per-call API
On the pay-per-call API the commit takes no payment, so
a package has to be bought at prepare: declare package=true on the prepare
call and the quote prices it in. A commit there asking for a package the quote
did not carry is refused with 400 "package was not quoted; prepare again with package=true".
Pricing
A package is billed once per commit that produces one, as one package at
2¢, which is flat, any length. The first
5 each month are free, and the free page cap is read
off the form's own pages, not the package's. A second commit on the same
proposal with package: true produces, and bills, a second package. See
Pricing for the full schedule.
Also available as a tool
add_attachment on MCP and the attach skill on
A2A take the same proposal_id, a file, and an optional for;
commit_proposal and the commit skill both take the same package
argument. See Read a filled form back for the
matching read-side call, and pricing for the free tier.
Once a submission is filed, batch fill is the pattern to reach for when the same form has to be filled, and packaged, many times over.