Skip to content
FrankKi
Sign inStart freeiOS Download
Menu

Complete letter job transcript

A whole letter job from the price question to the way back out, without the human ever leaving the conversation. The transcript shows a send with approval, because the approval queue is the most important step of a real send. It runs on a production token with the full_mcp bundle. The preconditions are a complete sender profile, the signed data processing agreement and funds in the wallet. We recorded this on 1 November 2026 against production, with a Stage account that prints nothing and runs on test money. Only mcp_health, wallet_balance and shipping_quote are real responses, and you can tell them by their requestId. All three are trimmed. From letter_create_draft on, the job writes data or sends for real, so the recording stops there. The later steps have no requestId and show the response shape with the price from the recorded quote.

  1. Setup: a tenant advice service uses a production token with the full_mcp bundle. From step 3 on this job writes data and sends for real: real cents, a real letter, a real human decision. The recording covers only the read-only calls up to the quote and ran on a Stage account, which is why mcp_health reports tokenMode "stage". To rehearse without sending, use dryRun as shown in the quickstart.
  2. Tool call
    mcp_health
  3. Result
    {
      "status": "ok",
      "code": "OK",
      "message": "OK",
      "data": {
        "ok": true,
        "tokenMode": "stage",
        "stageMode": true,
        "setup": {
          "ready": true
        }
      },
      "requestId": "req_G84A1HF31DQ4WAJP"
    }
  4. Step 1: is there money for this job at all? wallet_balance answers before anything is composed. If the balance were short, the next call would be wallet_topup_link, whose link the human opens once.
  5. Tool call
    wallet_balance
    {}
  6. Result
    {
      "status": "ok",
      "code": "OK",
      "message": "OK",
      "data": {
        "balanceEuros": "44,03",
        "reservedEuros": "0,00",
        "availableEuros": "44,03",
        "dailyCapLetters": 20,
        "dailyUsedLetters": 0,
        "dailyCapEuros": null,
        "dailyUsedEuros": "0,00",
        "nextResetAt": "2026-11-01T23:00:00.000Z",
        "standardLetter": {
          "unitPriceCents": 199,
          "unitPriceEuros": "1,99",
          "colorPricedAsGrayscale": true
        }
      },
      "requestId": "req_95PZBIW7AX085508"
    }
  7. Step 2: what would this cost? At this point there is no letter yet, only an intent: two pages, black and white, registered mail (Einwurf) inside Germany. That is exactly the question shipping_quote answers.
  8. Tool call
    shipping_quote
    {
      "pageCount": 2,
      "color": false,
      "deliveryType": "einschreiben_einwurf",
      "country": "DE"
    }
  9. Result
    {
      "status": "ok",
      "code": "OK",
      "message": "OK",
      "data": {
        "userCents": 509,
        "userPriceCents": 509,
        "userEuros": "5,09 EUR",
        "letterFormat": "standardbrief",
        "estimatedDeliveryRange": "ungefähr 1 bis 3 Werktage EN: approximately 1 to 3 working days",
        "deliveryProduct": {
          "code": "einschreiben_einwurf",
          "labelDe": "Einschreiben Einwurf",
          "labelEn": "Registered (drop-in)"
        },
        "source": "auto",
        "catalogCents": 659,
        "unitPriceCents": 509,
        "minMonthlyVolume": 0,
        "priceNote": "Endpreis. FrankKi ist Kleinunternehmer nach § 19 UStG und weist keine Umsatzsteuer aus. EN: Final price. FrankKi is a small business under § 19 UStG (German VAT Act) and charges no VAT."
      },
      "requestId": "req_YKSHKU8E2LQJE2RM"
    }
  10. A Stage account prints nothing and prices registered mail on a different route than a live account, hence 5,09 euros for two pages here. A live account at the entry tier pays 5,99 euros for a one-page Einwurf, as /preise/b2b shows, and each extra page costs a little more. The steps below use the 5,09 from this quote. priceNote is the same for everyone: final price, no VAT under § 19 UStG.
  11. Step 3: the letter itself. letter_create_draft composes the body into a DIN 5008 PDF and returns a letterId plus a preview link. Nothing is charged and nothing is sent. totalCents here prices the default standard letter, not the registered mail chosen above; the binding figure arrives with the send.
  12. Tool call
    letter_create_draft
    {
      "subject": "Widerspruch gegen die Betriebskostenabrechnung 2025",
      "content": "Sehr geehrte Damen und Herren,\n\nhiermit widerspreche ich der Betriebskostenabrechnung für das Jahr 2025 vom 02.06.2026. Die abgerechneten Kosten für die Gartenpflege sind gegenüber dem Vorjahr um 240 Prozent gestiegen, ohne dass ein Grund erkennbar wäre. Die Belege sind der Anlage zu entnehmen.\n\nMit freundlichen Grüßen\nAnna Berger",
      "recipientName": "Hausverwaltung Lindner GmbH",
      "recipientStreet": "Gartenstraße",
      "recipientHouseNumber": "18",
      "recipientZip": "04103",
      "recipientCity": "Leipzig",
      "recipientCountry": "DE"
    }
  13. Result
    {
      "status": "ok",
      "code": "OK",
      "message": "Entwurf angelegt.",
      "data": {
        "letterId": "7d2a1c50-4b8e-4c31-9f0a-3e6b5d1c8a90",
        "wordCount": 84,
        "pageCount": 1,
        "previewUrl": "https://storage.frankki.app/previews/7d2a1c50-4b8e-4c31-9f0a-3e6b5d1c8a90.pdf?X-Amz-Expires=86400",
        "deliveryType": "standard",
        "deliveryTypeLabel": "Standardbrief",
        "totalCents": 199,
        "totalEuros": 1.99,
        "totalFormatted": "1,99 €"
      }
    }
  14. Step 4: the evidence. attachment_upload_pdf appends the receipts to the same letterId. The merged page count is checked before the file is stored, so an oversized attachment fails here and not halfway through a send.
  15. Tool call
    attachment_upload_pdf
    {
      "letterId": "7d2a1c50-4b8e-4c31-9f0a-3e6b5d1c8a90",
      "title": "Belege Gartenpflege 2025",
      "pdfUrl": "https://files.example.de/belege-gartenpflege-2025.pdf"
    }
  16. Result
    {
      "status": "ok",
      "code": "OK",
      "message": "Anhang gespeichert.",
      "data": {
        "attachmentId": "c48f0a97-2d15-4e6b-9a83-70cb1e5d4f28",
        "pageCount": 1,
        "fileSize": 184320
      }
    }
  17. Step 5: the send. Note what comes back: orderId is null and status is "pending_approval". A live MCP send does not dispatch, it queues. The human is notified and the returned previewUrl plus costEuros are exactly what they are shown before deciding.
  18. Tool call
    order_send
    {
      "clientOrderId": "widerspruch-bk-2025-berger",
      "letterId": "7d2a1c50-4b8e-4c31-9f0a-3e6b5d1c8a90",
      "deliveryType": "einschreiben_einwurf"
    }
  19. Result
    {
      "status": "ok",
      "code": "OK",
      "message": "Freigabe angefordert.",
      "data": {
        "approvalId": "2f6c8b13-05a4-4d9e-8c17-b40e9a3f5d62",
        "orderId": null,
        "letterId": "7d2a1c50-4b8e-4c31-9f0a-3e6b5d1c8a90",
        "costCents": 509,
        "costEuros": 5.09,
        "deliveryType": "einschreiben_einwurf",
        "deliveryTypeLabel": "Einschreiben Einwurf",
        "previewUrl": "https://storage.frankki.app/previews/7d2a1c50-4b8e-4c31-9f0a-3e6b5d1c8a90-final.pdf?X-Amz-Expires=86400",
        "mode": "review",
        "expiresAt": "2026-07-25T09:22:41Z",
        "status": "pending_approval",
        "isReplay": false,
        "fulfillmentProvider": "frankki",
        "einlieferungsbelegPending": true
      }
    }
  20. Step 6: the decision. Before calling approve the agent shows the human the preview PDF, the exact 5,09 euros and the delivery type, and only then sets user_saw_preview:true. A chat approval does not dispatch immediately: dispatchAt is ten minutes out and cancellable stays true until then, so "stop" still works.
  21. Tool call
    approval_decide
    {
      "approvalId": "2f6c8b13-05a4-4d9e-8c17-b40e9a3f5d62",
      "decision": "approve",
      "user_saw_preview": true,
      "comment": "Anna hat Vorschau, Preis 5,09 Euro und Einschreiben Einwurf bestätigt."
    }
  22. Result
    {
      "status": "ok",
      "code": "OK",
      "message": "Freigabe erteilt.",
      "data": {
        "approvalId": "2f6c8b13-05a4-4d9e-8c17-b40e9a3f5d62",
        "orderId": "e91b4d07-6c22-4a8f-b3d5-19f7c0a2e441",
        "status": "approved",
        "decidedBy": "mcp:token_4f1a",
        "decidedAt": "2026-07-25T09:14:02Z",
        "approvedVia": "chat",
        "dispatchAt": "2026-07-25T09:24:02Z",
        "cancellable": true,
        "itemCount": 1,
        "totalCents": 509,
        "totalEuros": 5.09,
        "totalFormatted": "5,09 €",
        "deliveryType": "einschreiben_einwurf",
        "deliveryTypeLabel": "Einschreiben Einwurf",
        "isReplay": false
      }
    }
  23. Step 7: tracking. The approval produced the orderId; order_status follows it from here. Poll it again later for the delivery events, and for registered mail the tracking code appears as soon as the provider assigns it.
  24. Tool call
    order_status
    {
      "orderId": "e91b4d07-6c22-4a8f-b3d5-19f7c0a2e441"
    }
  25. Result
    {
      "status": "ok",
      "code": "OK",
      "message": "Status geladen.",
      "data": {
        "status": "processing",
        "timeline": [
          {
            "source": "frankki",
            "title": "Auftrag angelegt",
            "occurredAt": "2026-07-25T09:24:02Z"
          },
          {
            "source": "frankki",
            "title": "An den Versandpartner übergeben",
            "occurredAt": "2026-07-25T09:26:11Z"
          }
        ],
        "trackingCode": null,
        "cancellable": true
      }
    }
  26. Recovery branch A, cancel. A second letter from the same day is stopped before it is printed. Money goes back onto the wallet as a credit, never onto a card: FrankKi runs a closed loop. Once the letter is printing, order_cancel refuses cleanly instead of pretending.
  27. Tool call
    order_cancel
    {
      "orderId": "8a3e7f26-91d0-4b5c-a2e8-6d4f0b7c9153"
    }
  28. Result
    {
      "status": "ok",
      "code": "OK",
      "message": "Auftrag storniert.",
      "data": {
        "orderId": "8a3e7f26-91d0-4b5c-a2e8-6d4f0b7c9153",
        "status": "cancelled",
        "cancelled": true,
        "idempotent": false,
        "refundedEuros": 0,
        "walletCreditedEuros": 1.99,
        "costCents": 199
      }
    }
  29. Recovery branch B, fix and resubmit. A third letter came back from the provider with a rejected address. order_status names it: awaiting_partner_fix.
  30. Tool call
    order_status
    {
      "orderId": "46b0d9e5-73a2-4f18-8c6d-05e2a7b1f394"
    }
  31. Result
    {
      "status": "ok",
      "code": "OK",
      "message": "Status geladen.",
      "data": {
        "status": "awaiting_partner_fix",
        "timeline": [
          {
            "source": "frankki",
            "title": "Auftrag angelegt",
            "occurredAt": "2026-07-25T09:40:18Z"
          },
          {
            "source": "frankki",
            "title": "Adresse vom Versandpartner abgelehnt: Hausnummer fehlt",
            "occurredAt": "2026-07-25T09:41:55Z"
          }
        ],
        "cancellable": false
      }
    }
  32. The agent supplies the corrected recipient and the order is re-composed and re-enqueued. Nothing is charged a second time: the money already booked for this order stays where it is.
  33. Tool call
    order_fix_resubmit
    {
      "orderId": "46b0d9e5-73a2-4f18-8c6d-05e2a7b1f394",
      "recipient": {
        "name": "Stadtwerke Leipzig GmbH",
        "street": "Arno-Nitzsche-Straße",
        "houseNumber": "35",
        "zip": "04277",
        "city": "Leipzig",
        "country": "DE"
      }
    }
  34. Result
    {
      "status": "ok",
      "code": "OK",
      "message": "Auftrag erneut eingereicht.",
      "data": {
        "orderId": "46b0d9e5-73a2-4f18-8c6d-05e2a7b1f394",
        "status": "processing",
        "resubmittedAt": "2026-07-25T09:47:30Z",
        "fulfillmentProvider": "frankki",
        "isReplay": false
      }
    }
  35. That is the whole job inside one conversation: check funds, quote, draft, attach, send, get human approval, track, and both ways back out. No step ejects the human into the dashboard, except the one step that should: the approval itself, which they can also decide there.

Tools used

Every tool here is fully documented in the tool reference (fields, scope, dryRun).

See the other transcript: Tax advisor

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.