Zum Inhalt springen
FrankKi
Sign inStart freeiOS Download

Safe evaluation and financial controls

Physical-mail API with sandbox and spending limits

Verify capability first, rehearse without mailing, and layer independent limits before giving an agent production access.

FrankKi source supports account-scoped Test-Tokens for simulated zero-cent sends with watermarked PDFs, test storage, simulated status, and no postal-provider submission. Sandbox availability is not universal and current account access is not measured. Call mcp_health and stop if the authenticated session does not confirm the expected test mode and sandbox capability. For production, combine minimum scopes, an exact quote, maxCostEuros, account or key caps, wallet balance, idempotency, and separate human approval.

Confirm the sandbox instead of assuming it

An administrator can create a Test-Token from the Probe access area of the dashboard only when the Backend grants that account sandbox access. The raw bearer token is shown once. Source enables access through a deployment flag or an active per-account stage-mode grant; this page does not claim that a particular production account currently has either.

Connect the Test-Token to https://mcp.frankki.app and call mcp_health. Continue only when the response confirms the authenticated test mode and sandbox capability. If the capability is absent, stop. Never fall through to a production credential or reinterpret an unavailable sandbox as permission to send live mail.

mcp_health({ "checkRender": true })

// Continue with a rehearsal only when the response confirms
// the expected authenticated test mode and sandbox capability.

Know exactly what the rehearsal exercises

In the checked-in source, a sandbox send costs zero cents, stores test data, generates watermarked PDFs, advances through simulated statuses, and does not submit to a postal provider. The normal data-processing gate still applies to order_send. A sandbox run proves the test path accepted the request; it does not prove that a production provider, live approval queue, live wallet debit, or destination product was exercised.

  • Test-Token is a credential mode, not the same thing as OAuth or a production API key.
  • dryRun is a non-dispatch execution option. It does not by itself grant sandbox access.
  • A preview renders content but is not a dispatch approval.
  • Simulated delivery status is not provider tracking or proof of delivery.

Start with minimum scopes

The generated scope vocabulary separates reads from writes. Useful reads include address:read, letter:read, wallet:read, order:read, approval:read, and archive:read. order:send enables send-side actions. approval:decide is a separate write scope and should remain off model-operated keys when a human-operated approval session can hold it instead.

The source bundles are read_only, send_only, and full_mcp, but examples should prefer an explicit minimum set. Existing grants do not automatically gain newly introduced scopes, and token administration stays behind the human dashboard session.

Layer price and spending controls

No single number is the complete budget boundary. Quote the actual destination and reviewed letter, carry the returned price fields into the send, and set a caller-owned ceiling. Then retain account controls and funding checks around it.

  • shipping_quote returns the current destination-aware estimate. A dry-run order_send is more precise for the final letter and attachments.
  • priceVersion and quotedUnitPriceCents make a changed price fail with PRICE_CHANGED before charge.
  • maxCostEuros is a per-call ceiling. It is not a daily budget or wallet balance.
  • Wallet checks can enforce daily letter and cent caps. A supplied client or Mandant can have a narrower monthly cap.
  • Sub-account controls should narrow account controls, never silently widen them.
  • Wallet balance and reservation logic prevent unfunded dispatch. MCP does not collect card data in the agent interaction.
shipping_quote({
  "letterId": "REVIEWED_LETTER_ID",
  "pageCount": 1,
  "color": false,
  "deliveryType": "standard",
  "country": "DE"
})

order_send({
  "letterId": "REVIEWED_LETTER_ID",
  "recipientName": "Example Recipient",
  "recipientStreet": "Example Street",
  "recipientHouseNumber": "1",
  "recipientZip": "10115",
  "recipientCity": "Berlin",
  "recipientCountry": "DE",
  "deliveryType": "standard",
  "priceVersion": "VERSION_FROM_QUOTE",
  "quotedUnitPriceCents": 123,
  "maxCostEuros": 5,
  "approvalMode": "review",
  "clientOrderId": "case-482-send-v1",
  "dryRun": true
})

Boundary: 123 is an illustrative placeholder, not a price claim. Use the exact value returned for the exact request. Do not automatically raise a limit, top up a wallet, change the postal product, or retry PRICE_CHANGED without a new quote and renewed approval.

Fail closed and retry idempotently

Derive clientOrderId from a stable business operation before the first call and persist it outside model text. A retry of the same operation reuses the same key and payload. Never respond to an unknown timeout with a new random key. If the payload changes under the same key, IDEMPOTENCY_CONFLICT is the expected stop signal.

Missing sandbox capability, an unavailable quote, PRICE_CHANGED, insufficient wallet funds, a reached cap, or a rejected approval are stop conditions. Surface the returned error and recovery path to a human. Do not silently downgrade delivery type or swap credentials.

Next steps

Related documentation and decisions

Trust and live status

Machine-readable service facts and the current operational status of the partner interface are publicly available at all times, no login needed.