Invoice template with online payment

Build & score

Most invoice templates are a document you fill in, export to PDF, and email into the void. This one is different: it's a complete QuizGen JSON definition. POST it (or hand it to an AI assistant) and you get a hosted invoice page at a short link — your client opens it, confirms the charges are right, approves, and pays by card without leaving the page. You get a timestamped approval and the payment in your own Stripe account.

Card payment inside the invoice is free on every plan but needs a one-time Stripe connection. Everything else here — the itemized invoice, the recorded approval, the paper trail — works without it, and there's a payment-link fallback below until you've connected Stripe.

How the flow works

  1. You (or your AI assistant) fill in the line items and POST the template.
  2. Your client gets a short link — https://quizgen.dev/q/x7km2p.
  3. They review the itemized charges, approve, and pay by card in place.
  4. The money lands in your Stripe account. The response records paid: true and the payment_intent id, so it reconciles cleanly against your Stripe dashboard.
  5. You read approvals back via GET /quizzes/:id/responses, or get a webhook the moment one completes.

An invoice, an approval record, and the payment — without asking the client to create an account or open an attachment.

The template

Replace the line items and amounts before posting. amount_cents is the total in cents, so $3,300.00 is 330000:

{
  "schema_version": 1,
  "kind": "form",
  "title": "Invoice #2026-014 — Acme Design Co.",
  "description": "Due within 14 days. Questions? Reply to the email this came in.",
  "settings": {
    "collect_respondent": "email_required",
    "completion": { "message": "Thanks — payment received. A receipt is on its way." }
  },
  "sections": [
    {
      "key": "invoice",
      "title": "Invoice details",
      "questions": [
        { "id": "line_items", "type": "statement",
          "title": "Invoice #2026-014 · due August 19, 2026",
          "body": [
            "Website redesign — homepage and pricing page: $2,400",
            "Contact form + CRM integration: $600",
            "Rush delivery (agreed July 28): $300",
            "Total due: $3,300"
          ] },
        { "id": "details_correct", "type": "yes_no",
          "prompt": "Do the charges above match what we agreed?",
          "required": true },
        { "id": "correction", "type": "long_text",
          "prompt": "What looks off? I'll send a corrected invoice.",
          "show_if": { "question": "details_correct", "op": "eq", "value": "no" } },
        { "id": "approval", "type": "consent",
          "prompt": "I approve this invoice for $3,300.",
          "show_if": { "question": "details_correct", "op": "eq", "value": "yes" },
          "required": true },
        { "id": "pay", "type": "payment",
          "prompt": "Pay this invoice",
          "amount_cents": 330000,
          "currency": "usd",
          "button_label": "Pay $3,300",
          "show_if": { "question": "details_correct", "op": "eq", "value": "yes" },
          "required": true }
      ]
    }
  ]
}

What each piece is doing:

  • kind: "form" — tells QuizGen this is a form rather than a quiz, so it renders as one page instead of one question at a time. Advisory only: anything you put in settings still wins.
  • collect_respondent: "email_required" — the approval is tied to your client's email address, so the response is attributable.
  • The statement holds the line items. It's display-only; respondents can't edit it, which is exactly what you want on an invoice.
  • details_correct + show_if — a "no" opens a correction box instead of pushing a confused client toward paying; a "yes" reveals the approval and the payment button.
  • consent — the approval is a required, timestamped agreement stored on the response, separate from the payment itself.
  • payment — the card field. Because it's hidden by show_if when the charges are disputed, and hidden questions are never required, a client who flags a problem can still submit without paying.

What you need for card payments

  • Any QuizGen account (free is fine), and Stripe onboarding finished once at https://quizgen.dev/dashboard/payments. Without it the create call returns 402 payments_not_ready.
  • Money goes to your own Stripe account — QuizGen never holds it. QuizGen takes a 2% platform fee; Stripe's own processing fees apply as normal.
  • One payment question per quiz. Use amount_cents for a fixed total, or price_options (2–10 entries of {value, label, amount_cents}) when the client picks — handy for "pay in full" versus "50% deposit now".

For the non-invoice cases — order forms, paid registrations, deposits taken alongside an intake questionnaire — the same payment question is covered more generally at https://quizgen.dev/form-with-payment.

If you haven't connected Stripe yet

Drop the pay question and send the client onward to a payment link you already have:

"completion": {
  "message": "Thanks — taking you to payment now.",
  "redirect_url": "https://buy.stripe.com/YOUR_PAYMENT_LINK"
}

You still get the itemized invoice and the recorded approval; you just reconcile "approved" against "paid" yourself.

Let an AI assistant do the filling in

The fastest path isn't editing JSON by hand. Connect QuizGen's MCP server and say:

Read https://quizgen.dev/blog/invoice-template.md and make me an invoice for Jordan at Acme: 12 hours of consulting at $150/hr and one $200 setup fee, due in 14 days, payable by card. Give me the link to send.

The assistant adapts the template, POSTs it, and hands back the live link — and can tell you later whether it's been paid.

Knowing when you've been paid

Set a webhook and each completed invoice POSTs itself to your endpoint — client email, answers, paid, and the payment_intent id — so you can route it to Slack, your CRM, or your bookkeeping without polling. The payment_intent is the same id you'll see in Stripe, which makes reconciliation a lookup rather than a guess.

FAQ

Is this a real invoice, legally? It's an itemized bill with a recorded, timestamped approval and a matching payment record — for most freelance and small-business work that's what matters. If your jurisdiction requires numbered tax invoices with specific fields, put them in the statement body and keep your own numbering.

Can I take a deposit instead of the full amount? Yes — use price_options with "50% deposit" and "Pay in full" as the two choices. A quote with a deposit is the natural step before this one.

Can I limit it to one client? Send the link only to them — email_required records who responded. Add "max_responses": 1 to settings if you want the link to close after the first completion.

Can it expire? Yes — "close_at": "2026-08-19T00:00:00Z" in settings closes it at the due date.

What about recurring invoices? POST the same template monthly with updated line items — one API call, or one sentence to your assistant.

$ curl quizgen.dev/blog/invoice-template.md

This guide as raw markdown — no HTML for your agent to parse.

View .md

Keep reading