Outcome
Render one saved invoice as a complete multi-page PDF using the published Brand kit bound to its Books workspace. Once online payment has been enabled, its QR opens the stable hosted invoice page, which offers checkout only while money remains due.
Before you start
- Verify the intended environment, authenticated identity and specifically approved organization context.
- The published application permission is invoices:write. Discover the exact granted canonical Books workspace and bind it separately from action input.
- Approve the actual mutation, accounting inputs and any provider/recipient effects before CLI --confirm or MCP confirm:true. Fixture identifiers and simulated provider responses authorize no live operation.
Discover the target
topolo whoami --json
topolo actions get app_topolo_books.invoices.pdf.create --detail --json
topolo resources topolo-books --json
topolo workspaces list topolo-books --json
- Set BOOKS_WORKSPACE_ID to the exact granted workspace returned by discovery.
- documentId: Exact document.id from invoices.list/get or bills.list/get, as appropriate to this action; the displayed invoice number is distinct.
Request
POST /api/invoices/{documentId}/pdf
Inspect and test this action. Save the reviewed input as payload.json; supply credential/resource context separately.
Input fields
| Field | Required | Meaning | Schema and constraints |
|---|---|---|---|
documentId | Required | Exact document.id from invoices.list/get or bills.list/get, as appropriate to this action; the displayed invoice number is distinct. | {"type":"string","minLength":1} |
Inspect nested fields and the complete input schema.
Effects
- Creates a Books-native PDF from the saved invoice, customer, invoice identity settings and the organization-owned published Brand kit bound to the exact Books workspace, without changing the invoice. The PDF records the resolved Brand kit and immutable version identifiers. Once online payment exists and the customer page is active, the QR remains stable because it opens that Books page rather than a provider checkout.
Interpret the response
This is the canonical SDK PDF byte projection, not a resource identifier. Retain the input documentId from invoices.get, decode contentBase64 and verify decoded contentLength. Never use Base64 bytes as an id.
Validate the returned application/pdf bytes and open the complete invoice document. Confirm its logo and palette match the Brand version recorded in the PDF metadata. Decode the QR and confirm it opens the active Books customer page, which shows checkout while due and a paid confirmation after settlement.
Inspect the success schema. CLI/MCP add their own transport envelope; CLI --detail requests the full result.
Worked examples
Actual Books route handlers and the exported Worker were exercised against five local SQLite D1 concern databases with all committed migrations, the real posting Durable Object and local R2. Route captures use synthetic verified Auth; Worker captures use synthetic Auth transports. Nexus, Brand, Notify, Pay, Mercury and Xero responses are explicitly simulated at the provider transport boundary. Captures prove local request/response behavior and canonical SDK projection, not live bank access, payment movement, email delivery, provider eligibility, tax compliance, encrypted custody, scheduled execution, deployment or production completion. Fixture identities, credentials, addresses and records authorize no live operation.
Run a reviewed request
Save the input from the selected worked example as payload.json. Validate and inspect the plan before calling the action; refused examples demonstrate errors and are not instructions to execute.
CLI:
topolo actions validate app_topolo_books.invoices.pdf.create --data @payload.json --json
topolo actions plan app_topolo_books.invoices.pdf.create --resource-type workspace --resource-id "$BOOKS_WORKSPACE_ID" --data @payload.json --json
topolo actions call app_topolo_books.invoices.pdf.create --resource-type workspace --resource-id "$BOOKS_WORKSPACE_ID" --data @payload.json --confirm --detail --json
MCP tool and arguments for the first successful example:
{
"tool": "topolo_call_action",
"arguments": {
"actionId": "app_topolo_books.invoices.pdf.create",
"input": {
"documentId": "doc_c693276c99d146cd9f55c0f94051841f"
},
"resourceType": "workspace",
"resourceId": "<exact granted workspace id>",
"confirm": true,
"detail": true
}
}
Refused response · auth_error
Input · payload.json:
{
"documentId": "doc_9f2a"
}
Captured response · HTTP 401
{
"error": "API key validation failed"
}
HTTP 401. SDK error code auth_error. Authenticate in the intended organization again. Verify whoami and the exact Books application/workspace binding before retrying; do not switch identity to work around a refusal.
Successful response · HTTP 200
Input · payload.json:
{
"documentId": "doc_c693276c99d146cd9f55c0f94051841f"
}
Captured response · HTTP 200
{
"contentType": "application/pdf",
"contentLength": 3118,
"contentBase64": "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"
}
HTTP 200. Validate the returned application/pdf bytes and open the complete invoice document. Confirm its logo and palette match the Brand version recorded in the PDF metadata. Decode the QR and confirm it opens the active Books customer page, which shows checkout while due and a paid confirmation after settlement.
Verify the result
- Validate the returned application/pdf bytes and open the complete invoice document. Confirm its logo and palette match the Brand version recorded in the PDF metadata. Decode the QR and confirm it opens the active Books customer page, which shows checkout while due and a paid confirmation after settlement.
Errors and recovery
auth_error(HTTP 401): Authenticate in the intended organization again. Verify whoami and the exact Books application/workspace binding before retrying; do not switch identity to work around a refusal.
These are observed local refusals. Shared authentication, schema validation and resource authorization can also refuse before the route. Preserve the actual error, details and request id.
Recovery boundary
PDF generation does not change the invoice. Correct the draft or identity settings and generate it again.
Related actions
- Get an invoice: Read one invoice with its lines and the payments against it.
- List invoices: List invoices newest first. Filter by status, contact or issue date, search the number, reference and notes, and sort by issue date, due date, number, total or status. The response carries the outstanding total for the filtered set.
Evidence
Actual Books route handlers and the exported Worker were exercised against five local SQLite D1 concern databases with all committed migrations, the real posting Durable Object and local R2. Route captures use synthetic verified Auth; Worker captures use synthetic Auth transports. Nexus, Brand, Notify, Pay, Mercury and Xero responses are explicitly simulated at the provider transport boundary. Captures prove local request/response behavior and canonical SDK projection, not live bank access, payment movement, email delivery, provider eligibility, tax compliance, encrypted custody, scheduled execution, deployment or production completion. Fixture identities, credentials, addresses and records authorize no live operation.