[{"data":1,"prerenderedAt":703},["ShallowReactive",2],{"guide-\u002Fdocs\u002Fguides\u002Foperations\u002Fmonitoring-usage":3,"guides-all":635},{"id":4,"title":5,"body":6,"category":626,"description":627,"extension":628,"icon":629,"meta":630,"navigation":333,"order":179,"path":631,"seo":632,"stem":633,"__hash__":634},"guides\u002Fdocs\u002Fguides\u002Foperations\u002Fmonitoring-usage.md","Monitoring usage & credits",{"type":7,"value":8,"toc":612},"minimark",[9,13,17,22,25,75,86,90,118,122,125,139,150,154,166,231,238,242,245,253,264,314,317,385,389,399,509,512,516,562,573,577,586,590,608],[10,11,5],"h1",{"id":12},"monitoring-usage-credits",[14,15,16],"p",{},"Transactional debits credits as you use the platform. This page is how to read them, alert on them, and decide what to do when you're running low.",[18,19,21],"h2",{"id":20},"the-two-counters","The two counters",[14,23,24],{},"Every account has two independent counters, each with a monthly grant from your plan plus any one-off top-ups you've purchased:",[26,27,28,41],"table",{},[29,30,31],"thead",{},[32,33,34,38],"tr",{},[35,36,37],"th",{},"Counter",[35,39,40],{},"Debited by",[42,43,44,65],"tbody",{},[32,45,46,53],{},[47,48,49],"td",{},[50,51,52],"strong",{},"Generation credits",[47,54,55,56,60,61,64],{},"Every successful ",[57,58,59],"code",{},"POST \u002Fv1\u002Fgenerate"," call (and ",[57,62,63],{},"generate_pdf"," via MCP). One credit per call. Failed renders refund automatically.",[32,66,67,72],{},[47,68,69],{},[50,70,71],{},"AI credits",[47,73,74],{},"Every assistant turn in the editor. Debited proportionally to tokens consumed (input + output + cache reads\u002Fwrites).",[14,76,77,78,81,82,85],{},"The dashboard's ",[50,79,80],{},"Statistics"," view shows both gauges with ",[57,83,84],{},"used \u002F included \u002F topUp \u002F remaining",". The top-of-page banner turns amber at 80% used and red at 100%.",[18,87,89],{"id":88},"where-to-look","Where to look",[91,92,93,100,106,112],"ul",{},[94,95,96,99],"li",{},[50,97,98],{},"Statistics → top of page"," — the two gauges, current period",[94,101,102,105],{},[50,103,104],{},"Statistics → consumption chart"," — 30-day timeline, with error count",[94,107,108,111],{},[50,109,110],{},"Statistics → recent calls"," — the last 10 API calls (status, route, latency, key used)",[94,113,114,117],{},[50,115,116],{},"Billing → subscription"," — plan, current period end, top-up history",[18,119,121],{"id":120},"reading-the-chart","Reading the chart",[14,123,124],{},"The 30-day chart shows total calls and errors per day. Two patterns to watch:",[91,126,127,133],{},[94,128,129,132],{},[50,130,131],{},"Steady baseline + sudden spike"," — a job loop with a bug (\"regenerate for all customers\"). Pause and investigate.",[94,134,135,138],{},[50,136,137],{},"Slow upward drift"," — your business is growing. Plan an upgrade before the cap.",[14,140,141,142,149],{},"The error rate is shown on the same chart. A green ~99%+ success rate is normal. Persistent 5xx during render time usually means Gotenberg saturation on our side — check ",[143,144,148],"a",{"href":145,"rel":146},"https:\u002F\u002Fstatus.transactional.dev\u002F",[147],"nofollow","status",".",[18,151,153],{"id":152},"what-happens-at-zero","What happens at zero",[14,155,156,158,159,165],{},[57,157,59],{}," returns ",[50,160,161,162],{},"402 ",[57,163,164],{},"quota_exceeded"," when generation credits are at zero:",[167,168,173],"pre",{"className":169,"code":170,"language":171,"meta":172,"style":172},"language-json shiki shiki-themes github-light github-dark","{\n  \"error\": \"quota_exceeded\",\n  \"type\": \"generation\",\n  \"message\": \"Generation credits exhausted. Upgrade or top up to continue.\"\n}\n","json","",[57,174,175,184,201,214,225],{"__ignoreMap":172},[176,177,180],"span",{"class":178,"line":179},"line",1,[176,181,183],{"class":182},"sVt8B","{\n",[176,185,187,191,194,198],{"class":178,"line":186},2,[176,188,190],{"class":189},"sj4cs","  \"error\"",[176,192,193],{"class":182},": ",[176,195,197],{"class":196},"sZZnC","\"quota_exceeded\"",[176,199,200],{"class":182},",\n",[176,202,204,207,209,212],{"class":178,"line":203},3,[176,205,206],{"class":189},"  \"type\"",[176,208,193],{"class":182},[176,210,211],{"class":196},"\"generation\"",[176,213,200],{"class":182},[176,215,217,220,222],{"class":178,"line":216},4,[176,218,219],{"class":189},"  \"message\"",[176,221,193],{"class":182},[176,223,224],{"class":196},"\"Generation credits exhausted. Upgrade or top up to continue.\"\n",[176,226,228],{"class":178,"line":227},5,[176,229,230],{"class":182},"}\n",[14,232,233,234,237],{},"The AI assistant returns the same 402 (with ",[57,235,236],{},"\"type\": \"ai\"",") when AI credits run out. The editor surfaces a CTA — the API itself just returns the error so your code can branch on it.",[18,239,241],{"id":240},"setting-alerts-in-your-own-infra","Setting alerts in your own infra",[14,243,244],{},"We don't push email or webhooks for credit thresholds today. Two ways to monitor from your side:",[246,247,249,250],"h3",{"id":248},"pull-based-get-v1me","Pull-based — ",[57,251,252],{},"GET \u002Fv1\u002Fme",[14,254,255,256,259,260,263],{},"Poll ",[57,257,258],{},"\u002Fv1\u002Fme"," from a cron (every few hours is plenty) and alert when ",[57,261,262],{},"usage.generation.remaining"," falls below a threshold:",[167,265,269],{"className":266,"code":267,"language":268,"meta":172,"style":172},"language-bash shiki shiki-themes github-light github-dark","curl -s -H \"x-api-token: $TRANSACTIONAL_API_TOKEN\" \\\n  https:\u002F\u002Fapi.transactional.dev\u002Fv1\u002Fme \\\n  | jq '.usage.generation.remaining'\n","bash",[57,270,271,295,302],{"__ignoreMap":172},[176,272,273,277,280,283,286,289,292],{"class":178,"line":179},[176,274,276],{"class":275},"sScJk","curl",[176,278,279],{"class":189}," -s",[176,281,282],{"class":189}," -H",[176,284,285],{"class":196}," \"x-api-token: ",[176,287,288],{"class":182},"$TRANSACTIONAL_API_TOKEN",[176,290,291],{"class":196},"\"",[176,293,294],{"class":189}," \\\n",[176,296,297,300],{"class":178,"line":186},[176,298,299],{"class":196},"  https:\u002F\u002Fapi.transactional.dev\u002Fv1\u002Fme",[176,301,294],{"class":189},[176,303,304,308,311],{"class":178,"line":203},[176,305,307],{"class":306},"szBVR","  |",[176,309,310],{"class":275}," jq",[176,312,313],{"class":196}," '.usage.generation.remaining'\n",[14,315,316],{},"In Python:",[167,318,322],{"className":319,"code":320,"language":321,"meta":172,"style":172},"language-python shiki shiki-themes github-light github-dark","import os, urllib.request, json\n\nreq = urllib.request.Request(\n    \"https:\u002F\u002Fapi.transactional.dev\u002Fv1\u002Fme\",\n    headers={\"x-api-token\": os.environ[\"TRANSACTIONAL_API_TOKEN\"]},\n)\nwith urllib.request.urlopen(req) as r:\n    data = json.loads(r.read())\n\nif data[\"usage\"][\"generation\"][\"remaining\"] \u003C 100:\n    send_pager_alert(\"Transactional generation credits below 100\")\n","python",[57,323,324,329,335,340,345,350,356,362,368,373,379],{"__ignoreMap":172},[176,325,326],{"class":178,"line":179},[176,327,328],{},"import os, urllib.request, json\n",[176,330,331],{"class":178,"line":186},[176,332,334],{"emptyLinePlaceholder":333},true,"\n",[176,336,337],{"class":178,"line":203},[176,338,339],{},"req = urllib.request.Request(\n",[176,341,342],{"class":178,"line":216},[176,343,344],{},"    \"https:\u002F\u002Fapi.transactional.dev\u002Fv1\u002Fme\",\n",[176,346,347],{"class":178,"line":227},[176,348,349],{},"    headers={\"x-api-token\": os.environ[\"TRANSACTIONAL_API_TOKEN\"]},\n",[176,351,353],{"class":178,"line":352},6,[176,354,355],{},")\n",[176,357,359],{"class":178,"line":358},7,[176,360,361],{},"with urllib.request.urlopen(req) as r:\n",[176,363,365],{"class":178,"line":364},8,[176,366,367],{},"    data = json.loads(r.read())\n",[176,369,371],{"class":178,"line":370},9,[176,372,334],{"emptyLinePlaceholder":333},[176,374,376],{"class":178,"line":375},10,[176,377,378],{},"if data[\"usage\"][\"generation\"][\"remaining\"] \u003C 100:\n",[176,380,382],{"class":178,"line":381},11,[176,383,384],{},"    send_pager_alert(\"Transactional generation credits below 100\")\n",[246,386,388],{"id":387},"reactive-branch-on-the-error","Reactive — branch on the error",[14,390,391,392,395,396,398],{},"Wrap your ",[57,393,394],{},"\u002Fv1\u002Fgenerate"," calls. When you see ",[57,397,164],{},", page the team:",[167,400,404],{"className":401,"code":402,"language":403,"meta":172,"style":172},"language-ts shiki shiki-themes github-light github-dark","try {\n  await generatePdf({documentId, variables})\n} catch (e) {\n  if ((e as TransactionalError).code === 'quota_exceeded') {\n    sentry.captureMessage('Out of Transactional credits', {level: 'error'})\n    throw e\n  }\n  throw e\n}\n","ts",[57,405,406,414,425,436,462,485,493,498,505],{"__ignoreMap":172},[176,407,408,411],{"class":178,"line":179},[176,409,410],{"class":306},"try",[176,412,413],{"class":182}," {\n",[176,415,416,419,422],{"class":178,"line":186},[176,417,418],{"class":306},"  await",[176,420,421],{"class":275}," generatePdf",[176,423,424],{"class":182},"({documentId, variables})\n",[176,426,427,430,433],{"class":178,"line":203},[176,428,429],{"class":182},"} ",[176,431,432],{"class":306},"catch",[176,434,435],{"class":182}," (e) {\n",[176,437,438,441,444,447,450,453,456,459],{"class":178,"line":216},[176,439,440],{"class":306},"  if",[176,442,443],{"class":182}," ((e ",[176,445,446],{"class":306},"as",[176,448,449],{"class":275}," TransactionalError",[176,451,452],{"class":182},").code ",[176,454,455],{"class":306},"===",[176,457,458],{"class":196}," 'quota_exceeded'",[176,460,461],{"class":182},") {\n",[176,463,464,467,470,473,476,479,482],{"class":178,"line":227},[176,465,466],{"class":182},"    sentry.",[176,468,469],{"class":275},"captureMessage",[176,471,472],{"class":182},"(",[176,474,475],{"class":196},"'Out of Transactional credits'",[176,477,478],{"class":182},", {level: ",[176,480,481],{"class":196},"'error'",[176,483,484],{"class":182},"})\n",[176,486,487,490],{"class":178,"line":352},[176,488,489],{"class":306},"    throw",[176,491,492],{"class":182}," e\n",[176,494,495],{"class":178,"line":358},[176,496,497],{"class":182},"  }\n",[176,499,500,503],{"class":178,"line":364},[176,501,502],{"class":306},"  throw",[176,504,492],{"class":182},[176,506,507],{"class":178,"line":370},[176,508,230],{"class":182},[14,510,511],{},"This is the simpler approach but it catches you at zero — by then your customers are seeing failures.",[18,513,515],{"id":514},"top-up-vs-upgrade","Top up vs. upgrade",[26,517,518,528],{},[29,519,520],{},[32,521,522,525],{},[35,523,524],{},"Situation",[35,526,527],{},"What to do",[42,529,530,538,546,554],{},[32,531,532,535],{},[47,533,534],{},"You hit zero once, end of a busy month",[47,536,537],{},"Buy a top-up pack. They never expire.",[32,539,540,543],{},[47,541,542],{},"You're hitting zero every month",[47,544,545],{},"Upgrade the plan. The monthly grant is cheaper per credit than top-ups.",[32,547,548,551],{},[47,549,550],{},"You expect a one-off spike (campaign, end-of-quarter invoices)",[47,552,553],{},"Top up. Easier to model the cost.",[32,555,556,559],{},[47,557,558],{},"You're growing 20%+ month over month",[47,560,561],{},"Upgrade — you'll cross the next tier soon anyway.",[14,563,564,565,568,569,572],{},"Buy from ",[50,566,567],{},"Billing → top-up"," (one-off) or ",[50,570,571],{},"Billing → change plan"," (subscription).",[18,574,576],{"id":575},"per-key-visibility","Per-key visibility",[14,578,579,580,583,584,149],{},"The API Keys page shows a 7-day usage sparkline per key. If you've issued multiple tokens (dev \u002F staging \u002F prod), this is how you spot the one that's burning credits. Each call also records the ",[57,581,582],{},"apiKeyId"," in the audit log — surfaced on ",[50,585,110],{},[18,587,589],{"id":588},"next-steps","Next steps",[91,591,592,600],{},[94,593,594],{},[50,595,596],{},[143,597,599],{"href":598},"\u002Fdocs\u002Fguides\u002Foperations\u002Fstoring-pdfs","Storing & serving the generated PDF →",[94,601,602],{},[50,603,604],{},[143,605,607],{"href":606},"\u002Fdocs\u002Fguides\u002Fauthoring\u002Fworking-with-ai","A first prompt to try with the AI assistant →",[609,610,611],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":172,"searchDepth":186,"depth":186,"links":613},[614,615,616,617,618,623,624,625],{"id":20,"depth":186,"text":21},{"id":88,"depth":186,"text":89},{"id":120,"depth":186,"text":121},{"id":152,"depth":186,"text":153},{"id":240,"depth":186,"text":241,"children":619},[620,622],{"id":248,"depth":203,"text":621},"Pull-based — GET \u002Fv1\u002Fme",{"id":387,"depth":203,"text":388},{"id":514,"depth":186,"text":515},{"id":575,"depth":186,"text":576},{"id":588,"depth":186,"text":589},"operations","Read the dashboard gauges, set sane alerts, and decide when to top up vs. upgrade.","md","gauge",{},"\u002Fdocs\u002Fguides\u002Foperations\u002Fmonitoring-usage",{"title":5,"description":627},"docs\u002Fguides\u002Foperations\u002Fmonitoring-usage","zVBSwboxAJWGjZroxEbZ-1-ewx23t32v9loWyJllkoY",[636,641,645,649,653,657,661,665,670,674,678,681,686,691,695,699,700],{"path":637,"title":638,"description":639,"category":640,"order":227},"\u002Fdocs\u002Fguides\u002Fai-mcp\u002Fchatgpt","ChatGPT (via Custom GPT Actions)","ChatGPT doesn't speak MCP natively yet — but you can give it the same powers through a Custom GPT pointed at the Transactional REST API.","ai-mcp",{"path":642,"title":643,"description":644,"category":640,"order":203},"\u002Fdocs\u002Fguides\u002Fai-mcp\u002Fclaude-code","Claude Code (CLI)","Connect Transactional to Claude Code so it can read your templates and generate PDFs from your terminal.",{"path":646,"title":647,"description":648,"category":640,"order":186},"\u002Fdocs\u002Fguides\u002Fai-mcp\u002Fclaude-desktop","Claude Desktop","Connect Transactional to Claude Desktop on macOS or Windows so Claude can read your templates and generate PDFs.",{"path":650,"title":651,"description":652,"category":640,"order":216},"\u002Fdocs\u002Fguides\u002Fai-mcp\u002Fcursor","Cursor","Wire Transactional into Cursor's MCP support so you can generate PDFs from inside your editor.",{"path":654,"title":655,"description":656,"category":640,"order":352},"\u002Fdocs\u002Fguides\u002Fai-mcp\u002Fgemini","Gemini Code Assist \u002F Gemini CLI","Connect Transactional to Google's Gemini agents through their MCP support.",{"path":658,"title":659,"description":660,"category":640,"order":358},"\u002Fdocs\u002Fguides\u002Fai-mcp\u002Ftools-reference","MCP tools reference","Every tool the Transactional MCP server exposes, with arguments, return shapes, and a prompt that typically triggers each one.",{"path":662,"title":663,"description":664,"category":640,"order":179},"\u002Fdocs\u002Fguides\u002Fai-mcp\u002Fuse-from-ai","Use Transactional from your AI assistant","Connect Transactional to Claude, Cursor, ChatGPT, or Gemini via MCP so your assistant can read your templates and generate PDFs directly.",{"path":666,"title":667,"description":668,"category":669,"order":186},"\u002Fdocs\u002Fguides\u002Fauthoring\u002Fdesign-for-pdf","Designing templates that survive PDF rendering","PDFs are static — drop the animations, oversample your canvas charts, lean on vectors. The rules that make a template look sharp at print resolution.","authoring",{"path":671,"title":672,"description":673,"category":669,"order":179},"\u002Fdocs\u002Fguides\u002Fauthoring\u002Fhandlebars","Handlebars cheat sheet","The exact subset of Handlebars supported in Transactional templates — variables, conditionals, loops, and what NOT to reach for.",{"path":675,"title":676,"description":677,"category":669,"order":203},"\u002Fdocs\u002Fguides\u002Fauthoring\u002Fmodeling-variables","Modeling your variables","When to make something a variable vs. inline. Keep the API contract small, your templates portable, and your integration code boring.",{"path":606,"title":679,"description":680,"category":669,"order":216},"Working with the AI assistant","Prompts and patterns to get good templates fast — what to ask, when to iterate, when to start over.",{"path":682,"title":683,"description":684,"category":685,"order":179},"\u002Fdocs\u002Fguides\u002Fgetting-started\u002Fquickstart","Quickstart — your first PDF in 5 minutes","Sign up, design a template, render your first PDF through the API. End-to-end in five minutes.","getting-started",{"path":687,"title":688,"description":689,"category":690,"order":179},"\u002Fdocs\u002Fguides\u002Fintegrations\u002Fnode-bun","Calling \u002Fv1\u002Fgenerate from Node.js & Bun","Production-grade integration using native fetch — retries, error handling, streaming the PDF to your storage.","integrations",{"path":692,"title":693,"description":694,"category":690,"order":186},"\u002Fdocs\u002Fguides\u002Fintegrations\u002Fphp-laravel","Calling \u002Fv1\u002Fgenerate from PHP & Laravel","cURL extension, Guzzle, or Laravel's HTTP client — render PDFs with retries and proper error handling.",{"path":696,"title":697,"description":698,"category":690,"order":203},"\u002Fdocs\u002Fguides\u002Fintegrations\u002Fpython","Calling \u002Fv1\u002Fgenerate from Python","urllib (stdlib), requests, or httpx with retry — render PDFs from Django, FastAPI, or any Python service.",{"path":631,"title":5,"description":627,"category":626,"order":179},{"path":598,"title":701,"description":702,"category":626,"order":186},"Storing & serving the generated PDF","The \u002Fv1\u002Fgenerate URL is signed and short-lived. Patterns for keeping the PDF around — your bucket, your CDN, your DB.",1780347734348]