Invoice template with online payment
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
- You (or your AI assistant) fill in the line items and POST the template.
- Your client gets a short link —
https://quizgen.dev/q/x7km2p. - They review the itemized charges, approve, and pay by card in place.
- The money lands in your Stripe account. The response records
paid: trueand thepayment_intentid, so it reconciles cleanly against your Stripe dashboard. - 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 insettingsstill wins.collect_respondent: "email_required"— the approval is tied to your client's email address, so the response is attributable.- The
statementholds 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 byshow_ifwhen 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_centsfor a fixed total, orprice_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.
Keep reading
- Order form template that takes paymentPick the product, pay by card, done — in one submission.
- Quote template your client can approve in one clickNot a document to email — a hosted quote with a recorded approval.
- Event registration form template (with paid tickets)Ticket tiers, a capacity cap, and card payment on the registration page.