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 needs the Business plan and a one-time Stripe connection. Everything else here — the itemized invoice, the recorded approval, the paper trail — works on any plan, and there's a payment-link fallback below for plans without native payments.

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

  • The Business plan, 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're not on Business 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