# Pay per call with MPP

The Machine Payments Protocol (MPP) lets an agent pay for a single Emboss
operation over plain HTTP, with no account. Emboss issues a standard MPP
payment challenge, you pay it with a stablecoin or a card, and the operation
runs. This page covers the [pay-per-call API](/docs/pay-per-call) in detail.

Ephemeral processing: source and output documents are deleted 60 to 70 minutes after the last activity on a form. The pay-per-call API always runs under ephemeral processing.

## The operations

Each operation takes a PDF as JSON (`pdf_url` or `pdf_base64`) or as a multipart upload. Prices below are base
prices for a short form; longer forms cost more, and the exact price for your
request is always in the payment challenge. See [Pricing](/pricing).

- `POST https://api.getemboss.ai/pay/make-fillable`: turn a flat or scanned
  PDF form into a fillable PDF with detected fields (from $0.05).
- `POST https://api.getemboss.ai/pay/fill-with-context`: fill a PDF form
  using facts found in supporting documents (from $0.08).
- `POST https://api.getemboss.ai/pay/fill`: fill a PDF form from structured
  data such as JSON or key-value answers (from $0.07).
- `POST https://api.getemboss.ai/pay/read`: read a filled PDF's form boxes
  back as labelled values (from $0.01). Anonymous: the
  uploaded file is held until the read is paid for (deleted with the price if
  no payment arrives within the hour) and deleted as soon as the read returns;
  the values read stay on the quote for 24 hours so a lost answer can be
  fetched again.
- `POST https://api.getemboss.ai/pay/fax`: send a PDF to a fax number
  (`to` in E.164), priced per transmitted page. In place of a PDF, send the
  `artifact_id` of a file from an earlier paid operation together with its
  `artifact_token`, or a `sources` list to fax several artifacts as one
  packet; see [Artifacts and lineage](/docs/artifacts).

For what each operation does and returns, see the
[MCP tools reference](/docs/mcp-tools); the pay-per-call API runs the same engine.

## Check the price first

Every operation is priced by page count, so you can ask for the exact price of
your PDF for free before paying. The check is stateless: it reserves nothing
and creates no job.

For a one-page form and a short note:

```bash
curl https://api.getemboss.ai/pay/quote \
  -F "file=@form.pdf" \
  -F "context_text=Grace Hopper, born 9 December 1906, 1 Navy Way, Arlington, VA 22202."
```

```json
{
  "already_fillable": false,
  "card_minimum_usd": "0.50",
  "context_pages": 1,
  "expires": null,
  "methods": [
    "tempo",
    "stripe",
    "x402"
  ],
  "next": "POST /pay/<op> with the same file. The 402 challenge carries this exact price.",
  "pages": 1,
  "prices": {
    "fax": {
      "atomic": "30000",
      "cents": 3,
      "usd": "0.03"
    },
    "fill": {
      "atomic": "70000",
      "cents": 7,
      "usd": "0.07"
    },
    "fill-with-context": {
      "atomic": "80000",
      "cents": 8,
      "usd": "0.08"
    },
    "make-fillable": {
      "atomic": "50000",
      "cents": 5,
      "usd": "0.05"
    },
    "read": {
      "atomic": "10000",
      "cents": 1,
      "usd": "0.01"
    }
  }
}
```

The same check with a JSON body, for clients that cannot send multipart:

```bash
curl https://api.getemboss.ai/pay/quote \
  -H "content-type: application/json" \
  -d '{"pdf_url": "https://example.com/form.pdf"}'
```

`pdf_base64` (the PDF as standard base64) works in place of `pdf_url`. For
`/pay/fill`, `values` is a JSON object in the body; for
`/pay/fill-with-context`, supporting documents go in `context_base64` (a
list of `{"name", "base64"}`), `context_text`, or `context_urls`.

Send the same context files with the check to price `fill-with-context`
accurately. If the PDF already has form fields, the response says so and
`make-fillable` is not needed.

## Paying

Send the request without payment first. Emboss answers `402 Payment
Required` with a `WWW-Authenticate: Payment` header that carries one
challenge per payment method. Pay one of them, then resend the identical
request with the `Authorization: Payment` credential. Emboss verifies the
payment and responds `202 Accepted` with a `job_id`, a `status_url` to poll,
and a `Payment-Receipt` header.

An `Authorization` header that does not use the `Payment` scheme is answered with a fresh 402 rather than a 400: the challenge tells the caller how to pay, and nothing is parsed or claimed.

Two payment methods are offered on every challenge:

- **Tempo stablecoin (USDC.e).** You pay exactly the quoted price on the
  Tempo network.
- **Stripe Shared Payment Token (card via Link).** Card payments have a
  minimum of 50 cents, so a card pays 50 cents for an operation quoted below that;
  the challenge states the amount you will be charged.

Standard MPP clients handle the whole exchange for you. Because every
operation uploads a PDF, send the PDF as JSON (`pdf_url` or `pdf_base64`), or use a client that sends the file as multipart bytes.

Python, with the `mpp` package (`pip install "pympp[tempo]"`):

```python
import asyncio
from mpp.client import post
from mpp.methods.tempo import tempo, TempoAccount, ChargeIntent

method = tempo(
    account=TempoAccount.from_env("TEMPO_PRIVATE_KEY"),
    intents={"charge": ChargeIntent()},
)
r = asyncio.run(post(
    "https://api.getemboss.ai/pay/make-fillable",
    methods=[method],
    files={"file": ("form.pdf", open("form.pdf", "rb"), "application/pdf")},
))
job = r.json()  # 202: job_id, status_url, quote_id
```

JavaScript, with `mppx` (`npm i mppx viem`):

```js
import { readFileSync } from "node:fs";
import { privateKeyToAccount } from "viem/accounts";
import { Mppx, tempo } from "mppx/client";

const mppx = Mppx.create({
  methods: [tempo({ account: privateKeyToAccount(process.env.TEMPO_PRIVATE_KEY) })],
});

const form = new FormData();
form.append("file", new Blob([readFileSync("form.pdf")], { type: "application/pdf" }), "form.pdf");

const r = await mppx.fetch("https://api.getemboss.ai/pay/make-fillable", { method: "POST", body: form });
const job = await r.json(); // 202: job_id, status_url, quote_id
```

Both pay the challenge and return the `202` body. The paid call answers
within about 10 seconds: `status` is `ready` when the job finished in that
time, `working` when it is still running, or `failed`. Check `status_url`
every few seconds until `status` is `ready` or `failed`, then download the
finished PDF from `result.download_url`. The ready result also carries `artifact_id` and
`artifact_token`: send both to a later paid operation, such as
[`/pay/fax`](#the-operations), to reuse the file without paying to upload it
again. See [Artifacts and lineage](/docs/artifacts).

> The tempo CLI's `--data @file` flag is text-only: it strips line endings
> from the body and corrupts a PDF's compressed streams. Use one of the
> clients above for file uploads.

### Checking on a job

The `402` already carries the job's `job_id` and `status_url`, so you hold the
link to your result before you pay. `GET status_url` is free and answers one
word in `status`:

- `awaiting_payment`: priced, not yet paid.
- `working`: still running. Check again after `poll_after_seconds` (also sent
  as a `Retry-After` header).
- `ready`: finished. `result` carries the output and, for a job that produces
  a file, `result.download_url`: the same link on every check.
- `failed`: the job did not finish. `error` says why, and `retry`, when
  present, says how to run it again at no charge.
- `expired`: the result's files are deleted, about an hour after the job,
  under ephemeral processing; or a price that was never paid has expired, and
  sending the request again gets a new one.

`quote_status` keeps the older word (`quoted`, `claimed`, `executed` or
`expired`) for clients that read it.

For a fax, `ready` means the carrier accepted it; keep checking until
`result.status` is `delivered`, `failed` or `expired`. A failed or expired fax
is refunded: by card automatically, up to three refunds a day, and by
stablecoin through contact@getemboss.ai.

If the answer to the paid call is lost, check the `status_url` from the
`402`, or resend the identical request with the same payment credential:
nothing is charged again, and you get the job back. A job that failed runs
again at no charge when you resend it the same way, up to three runs in all
for one payment; after the third failed run it stays `failed`, and
contact@getemboss.ai can help with the job id. A resend after the result
expired is answered `410 Gone`: a new run needs a new payment. A resend must
carry the same document and settings as the first call; a different one is
refused with `409`.

A resend, a free retry included, works only while its payment challenge is
still valid (the challenge's `expires` says until when). After that, a resend
gets a new price, which a client that pays automatically would pay a second
time. Check the `status_url` instead: a job that failed then no longer offers
`retry`, and contact@getemboss.ai can help with the job id.

### Two optional fields on fill-with-context

`POST https://api.getemboss.ai/pay/fill-with-context` takes two optional form
fields alongside the file.

`policy` is `safe` or `strict`. `safe` is the default and writes any value the
engine reported as high or medium confidence; `strict` writes only high
confidence values and leaves the rest open.

`mode` is `one_call` or `two_call`. `one_call` is the default and returns the
finished PDF. `two_call` stops at the proposal, so a payer can read every
proposed answer before anything is written into the document. When the job
finishes within the paid call's wait, the `202` carries three additive keys
alongside the usual `job_id`, `status_url`, `quote_id` and `status`:

```json
{
  "job_id": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "status_url": "https://api.getemboss.ai/pay/jobs/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d?token=<token>",
  "quote_id": "f28119f5-0f99-4226-b596-7df0cd7cb197",
  "status": "ready",
  "proposal_id": "3e714663-ff2c-45ac-ae11-3c7015facfef",
  "commit_token": "<opaque token>",
  "proposal_url": "https://api.getemboss.ai/proposals/3e714663-ff2c-45ac-ae11-3c7015facfef?token=<opaque token>"
}
```

Otherwise the paid call answers `working`, and these same three keys come in
the status check's answer once `status` is `ready`.

Read the proposal at `proposal_url`, then commit it with
`POST https://api.getemboss.ai/proposals/{proposal_id}/commit`, sending the token
in an `X-Proposal-Token` header. The token proves you own the proposal you paid
for and lives 45 minutes. Committing costs nothing more: the work was billed
when you paid for the prepare.

```bash
curl -X POST https://api.getemboss.ai/proposals/3e714663-ff2c-45ac-ae11-3c7015facfef/commit \
  -H "X-Proposal-Token: <commit_token>" \
  -H "Content-Type: application/json" \
  -d '{"values": [{"field_id": 14, "value": "12-3456789"}]}'
```

See [Prepare, commit and verify](/docs/prepare-commit-verify) for the proposal
shape, the three results and the receipt. If two-call mode is not enabled on the
service, `mode=two_call` returns a `503`; every other path is unaffected.

## Billing and privacy

Pay-per-call operations are paid per request and do not use an Emboss
account. If you use the regular API with an API key instead, the account
billing applies: The first 5 form creations, 5 context fills, 5 standard fills, 5 verifications, 5 reads, and 5 packages each month are free when the job is 5 pages or fewer (a context fill counts the form and its documents together); a longer job is charged and does not use a free one. Only
the uploaded PDF and any context you attach are processed for the request;
see the [privacy policy](/privacy).

## See also

- [Fill PDFs from your own data](https://getemboss.ai/use-cases/fill-pdfs-from-your-data)
- [MCP server](https://getemboss.ai/glossary/mcp-server)
- [Pay per job with x402](https://getemboss.ai/docs/pay-per-call/x402)
- [Pay per call](https://getemboss.ai/docs/pay-per-call)
- [Pay per job with x402](https://getemboss.ai/docs/pay-per-call/x402)
- [MCP tools reference](https://getemboss.ai/docs/mcp-tools)
- [Use Emboss from agent frameworks (A2A)](https://getemboss.ai/docs/a2a)
