ScreenshotNeo

BlogHow-to

How to generate GST invoices as PDFs with DocRaptor

Build a GST invoice from validated data, render it as HTML, and send it to DocRaptor for PDF conversion. Learn where PDF generation ends and e-invoicing begins.

By the ScreenshotNeo team4 October 202615 min read

To generate a GST invoice PDF with DocRaptor, first build and validate the invoice record in your application, render it as HTML, and submit that HTML to DocRaptor’s PDF API. DocRaptor converts the supplied content into a document; it does not decide the tax treatment, validate every legal invoice condition, or obtain an Invoice Reference Number (IRN). If e-invoicing applies to the transaction, complete the applicable GST portal or IRP process separately and include its returned invoice data and QR representation in the PDF as appropriate.

This guide covers the document-generation workflow and its boundaries. It is implementation guidance, not a determination of whether a particular supplier or transaction is subject to an invoice requirement or e-invoicing mandate. Have a qualified GST professional check the applicable particulars and current notifications.

1. Separate invoice data, compliance checks, and PDF rendering

Keep the invoice record in your trusted billing or data layer. Store the supplier and recipient identities, invoice number and date, line items, taxable values, tax breakdown, and any e-invoice response fields as structured data. Validate applicable fields before building the document. The HTML template controls presentation; it should not be the only place where invoice values or tax decisions exist.

  1. Prepare the invoice record. Assign the number and date in the application and calculate amounts from your established billing rules.
  2. Validate the record. Check the applicable Rule 46 particulars and transaction conditions, along with application-level checks such as financial-year number uniqueness and date validity.
  3. Complete any required e-invoice workflow. Where Rule 48(4) applies, follow the applicable portal or IRP process and use the returned data, including the IRN and QR representation as required.
  4. Render the record as HTML. Escape user-provided values, format amounts and dates consistently, and include the particulars applicable to this supply.
  5. Submit the HTML to DocRaptor. Check the response status and save the returned PDF bytes or handle the selected hosted/asynchronous workflow.
  6. Deliver the document through your application. Use your own access controls and retention policy, checked against applicable rules and business requirements.

Rule 46 has conditional particulars, including details for certain recipients, exports or SEZ supplies, and invoices prepared under the Rule 48(4) e-invoice process. One static template should not be assumed to cover every supply. See the [official Rule 46 text](https://cbic-gst.gov.in/pdf/CGST-Rules-2017-Updated-01082021.pdf) and verify the applicable current rules and notifications.

2. Check the GST invoice particulars before rendering

Use this as a practical checklist, not an exhaustive legal opinion. Rule 46 is subject to Rule 54 and transaction-specific conditions.

Data to validate or render Implementation note
Supplier name, address, and GSTIN Load from the correct registered supplier or business unit record.
Invoice serial number and issue date Rule 46 describes a consecutive serial number, unique for a financial year, no longer than 16 characters; permitted characters include letters, numerals, hyphen/dash, and slash. Enforce uniqueness in the data layer rather than relying on the PDF template.
Recipient details Include the recipient identity and GSTIN/UIN where registered. Unregistered-recipient particulars can depend on the transaction and value; apply the relevant conditions.
Supply description and classification Include the description and applicable HSN code for goods or services. For goods, include quantity and unit where applicable.
Values and tax Render total supply value, taxable value after applicable discount or abatement, and the applicable tax rate and amount particulars from validated calculations.
Conditional particulars Add the particulars that apply to exports, SEZ supplies, reverse-charge cases, or other relevant cases; verify them against the rule text and professional guidance.
E-invoice information For invoices issued through the applicable Rule 48(4) process, include the returned IRN/QR information as required. Do not fabricate an IRN or use a decorative QR code as a substitute.

The GST Portal’s IFF manual says invoice dates cannot be future dates or precede GST registration, and invoice numbers should be unique within a financial year. Treat these as useful input checks and consult the [GST Portal manual](https://tutorial.gst.gov.in/userguide/returns/Manual_IFF.htm) and applicable rules for the issuer and supply type.

3. Understand when e-invoicing is a separate step

PDF conversion does not itself complete e-invoice reporting. Under Rule 48(4), notified classes of registered persons prepare invoices using Form GST INV-01 particulars and obtain an IRN by uploading invoice information to the common GST portal. Rule 48(5) says an invoice issued by a person subject to sub-rule (4) in another manner is not treated as an invoice. Rule 46 includes an IRN-embedded QR-code provision for invoices issued by that method. Review the [CGST Act](https://cbic-gst.gov.in/pdf/CGST-Act-2017.pdf), [Rule 46](https://cbic-gst.gov.in/pdf/CGST-Rules-2017-Updated-01082021.pdf), and [Rule 48](https://cbic-gst.gov.in/pdf/CGST-Rules-2017-Updated-01082021.pdf) against current amendments and your circumstances.

DocRaptor’s QR tutorial explains that a QR code can be included in HTML as an image, but DocRaptor does not generate the QR code itself. For a covered taxpayer, render the QR image or data supplied by the relevant e-invoice workflow. The E-Invoice Registration Portal describes direct API integration as suitable for taxpayers with internal IT teams, large transaction volumes, and a need for real-time generation. Its IRIS IRP release note states that taxpayers with aggregate annual turnover (AATO) of ₹10 crore or more must report covered invoices, credit notes, and debit notes within 30 days from invoice date, effective 1 April 2025. This is a scoped, dated notice, not a complete statement of current mandate applicability; check current official notifications and the taxpayer’s applicability at the [E-Invoice Registration Portal](https://einvoice.gst.gov.in/).

4. Create an HTML template that prints cleanly

The following is a layout skeleton, not a complete or universally compliant GST invoice. Populate it only from validated records and add the fields and conditional sections that apply to your transaction. Escape values before inserting them into HTML; do not concatenate untrusted input into markup.

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <style>
    @page { size: A4; margin: 16mm; }
    body { font: 10pt/1.4 Arial, sans-serif; color: #222; }
    h1 { font-size: 18pt; margin: 0 0 12px; }
    .parties { display: flex; gap: 24px; margin: 18px 0; }
    .party { flex: 1; }
    table { width: 100%; border-collapse: collapse; }
    th, td { border: 1px solid #bbb; padding: 6px; text-align: left; }
    thead { display: table-header-group; }
    tr { break-inside: avoid; }
    .totals { margin: 18px 0 0 auto; width: 45%; }
  </style>
</head>
<body>
  <h1>Tax Invoice</h1>
  <p>Invoice number: {{ escaped_invoice_number }}<br>
     Issue date: {{ formatted_issue_date }}</p>
  <div class="parties">
    <section class="party"><strong>Supplier</strong><br>
      {{ escaped_supplier_name }}<br>{{ escaped_supplier_address }}<br>
      GSTIN: {{ escaped_supplier_gstin }}
    </section>
    <section class="party"><strong>Recipient</strong><br>
      {{ escaped_recipient_name }}<br>{{ escaped_recipient_address }}<br>
      GSTIN/UIN: {{ escaped_recipient_id_if_applicable }}
    </section>
  </div>
  <table>
    <thead><tr><th>Description / HSN</th><th>Qty / unit</th>
      <th>Taxable value</th><th>Tax rate</th><th>Tax amount</th></tr></thead>
    <tbody>{{ escaped_line_item_rows }}</tbody>
  </table>
  <section class="totals">
    <p>Taxable total: {{ formatted_taxable_total }}</p>
    <p>Tax breakdown: {{ formatted_tax_breakdown }}</p>
    <p>Invoice total: {{ formatted_invoice_total }}</p>
  </section>
  {{ applicable_export_or_other_particulars }}
  {{ irn_qr_markup_if_applicable }}
</body>
</html>

The placeholders above are illustrative template variables, not JavaScript or a DocRaptor feature. Use your templating system’s HTML escaping, and create table rows as escaped structured fields rather than accepting raw HTML from users. If inserting the QR image, ensure the source is reachable by the renderer or embed it in a supported form.

5. Send the document to DocRaptor

DocRaptor’s REST API accepts JSON at https://api.docraptor.com/docs. Authenticate a direct REST request with HTTP Basic Authentication: the API key is the username and the password is blank. The API reference also documents query-parameter authentication. Keep the key server-side. Supply either document_content with HTML or document_url with retrievable content. The example below sends HTML and requests a direct PDF response.

cURL

curl --fail-with-body --silent --show-error \
  --user "$DOCRAPTOR_API_KEY:" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/pdf' \
  --data-binary @- \
  https://api.docraptor.com/docs \
  <<'JSON' \
{
  "type": "pdf",
  "test": true,
  "document_content": "<!doctype html><html><head><meta charset=\"utf-8\"><style>@page{size:A4;margin:16mm}body{font:10pt Arial}</style></head><body><h1>Tax Invoice</h1><p>Replace this example content with your escaped, validated invoice HTML.</p></body></html>"
}
JSON

That command writes the response body to standard output. For a production script, write successful PDF bytes to a file and route non-success responses to error handling; avoid saving an API error body with a .pdf extension. The test option is enabled here for layout iteration; test PDFs carry a watermark and are not final invoices.

Python

import os
import requests

api_key = os.environ["DOCRAPTOR_API_KEY"]
html = """<!doctype html>
<html><head><meta charset="utf-8">
<style>@page { size: A4; margin: 16mm; } body { font: 10pt Arial; }</style>
</head><body><h1>Tax Invoice</h1>
<p>Replace with HTML rendered from the validated invoice record.</p>
</body></html>"""
payload = {
    "type": "pdf",
    "test": True,  # Remove or set appropriately for final generation.
    "document_content": html,
}
response = requests.post(
    "https://api.docraptor.com/docs",
    auth=(api_key, ""),
    json=payload,
    headers={"Accept": "application/pdf"},
    timeout=120,
)
if not response.ok:
    raise RuntimeError(f"DocRaptor returned HTTP {response.status_code}: {response.text}")
if not response.content.startswith(b"%PDF-"):
    raise RuntimeError("Successful response did not look like a PDF")
with open("invoice.pdf", "wb") as pdf_file:
    pdf_file.write(response.content)
print("Saved invoice.pdf; pages:", response.headers.get("X-DocRaptor-Num-Pages", "unknown"))

Node.js

const apiKey = process.env.DOCRAPTOR_API_KEY;
if (!apiKey) throw new Error('Set DOCRAPTOR_API_KEY in the environment');

const html = `<!doctype html>
<html><head><meta charset="utf-8">
<style>@page { size: A4; margin: 16mm; } body { font: 10pt Arial; }</style>
</head><body><h1>Tax Invoice</h1>
<p>Replace with HTML rendered from the validated invoice record.</p>
</body></html>`;

const basic = Buffer.from(`${apiKey}:`).toString('base64');
const response = await fetch('https://api.docraptor.com/docs', {
  method: 'POST',
  headers: {
    'Authorization': `Basic ${basic}`,
    'Content-Type': 'application/json',
    'Accept': 'application/pdf',
  },
  body: JSON.stringify({ type: 'pdf', test: true, document_content: html }),
  signal: AbortSignal.timeout(120_000),
});
if (!response.ok) {
  throw new Error(`DocRaptor returned HTTP ${response.status}: ${await response.text()}`);
}
const bytes = Buffer.from(await response.arrayBuffer());
if (bytes.subarray(0, 5).toString() !== '%PDF-') {
  throw new Error('Successful response did not look like a PDF');
}
const { writeFile } = await import('node:fs/promises');
await writeFile('invoice.pdf', bytes);
console.log('Saved invoice.pdf; pages:', response.headers.get('X-DocRaptor-Num-Pages') ?? 'unknown');

These examples use short inline HTML so the request is self-contained. Replace it with HTML rendered from a validated invoice record. The Python and Node examples require a runtime with the shown APIs and an installed Python requests package. Set DOCRAPTOR_API_KEY as a server-side secret.

Choose content source and response mode

Choice Use it when Check
document_content or document_url Your application can send generated markup, or DocRaptor should fetch a rendered page. A URL and its assets must be retrievable by the rendering service. Verify relative asset URLs have a valid base and private resources are accessible through a deliberate mechanism.
Direct PDF response The application can wait for the render and receive bytes in the request. Handle the response as binary and check status before storing it.
Hosted document or asynchronous job Your delivery flow benefits from a returned document URL or a job/status step. Implement the follow-up retrieval/status handling described by the API response; do not assume the initial response body is PDF bytes.

See the [DocRaptor API overview](https://docraptor.com/documentation/api) and [API reference](https://docraptor.com/documentation/api) for current request and response fields, authentication, hosted documents, and asynchronous options.

6. Tune page size, CSS, and pagination

  • Set paper size deliberately. DocRaptor defaults to US Letter. For an A4 document, the documented CSS approach is @page { size: A4; }; set margins explicitly too.
  • Use print styles intentionally. Print media rules are applied by default. If the document looks like a screen layout or important styles disappear, check the intended media configuration and the CSS rules for that medium.
  • Choose how to load CSS. Embedded, inline, and external stylesheets are supported. Embedded CSS avoids a separate stylesheet fetch. For external stylesheets and images, ensure the renderer can retrieve them and that relative paths resolve from a valid base URL.
  • Plan long tables for page breaks. break-before, break-after, and break-inside can suggest pagination behavior. For example, tr { break-inside: avoid; } may help keep a row together, but the renderer can only follow suggestions when feasible. A block taller than a page cannot be kept on one page.
  • Repeat table headings. Use a table header group such as <thead> and inspect multi-page output so line items remain understandable.
  • Inspect page count. Direct PDF responses include X-DocRaptor-Num-Pages, which is useful for diagnostics such as unexpectedly large output.

See DocRaptor’s guides on [print media](https://docraptor.com/documentation/article/1067996-print-media), [page size](https://docraptor.com/documentation/article/1067997-page-size), [page breaks](https://docraptor.com/documentation/article/1067998-page-breaks), and [CSS](https://docraptor.com/documentation/article/1067995-css) for supported behavior and current syntax.

7. Test the rendering and promote only final output

  1. Use test mode while developing the template. DocRaptor says test documents do not count against monthly limits, but generated PDFs include a watermark.
  2. Check the output visually across representative invoice cases: one and many line items, long descriptions, conditional recipient details, applicable export or SEZ fields, and the e-invoice QR data where required.
  3. Check text extraction and values as well as appearance. Confirm the rendered number, date, quantities, amounts, tax breakdown, and totals agree with the source record.
  4. Exercise multi-page output, special characters, long addresses, missing optional fields, and external assets.
  5. Use the appropriate non-test configuration for final document generation. Do not deliver a watermarked test PDF as a final invoice.

The sources do not establish a particular business’s retention period or delivery policy. Decide those from applicable requirements and company policy, rather than assuming PDF generation handles retention.

8. Troubleshoot common problems

Symptom Likely cause Fix
HTTP authentication failure The key is absent, incorrect, or sent using the wrong authentication format. Use HTTP Basic with the API key as username and a blank password; check that the server-side secret is present and not expired or mistyped.
Response is an error body, not a PDF The request was rejected or the response mode differs from the direct-PDF assumption. Check HTTP status and response body before writing bytes. If using hosted or async mode, follow its URL/status flow.
PDF has a watermark Test mode is enabled. Use test mode only for layout iteration; switch to the appropriate non-test setting for final output.
Page is Letter-sized or content clips The default page size or margins do not match the intended layout. Set @page size and margins explicitly, then inspect the resulting page count and print preview.
CSS or images are missing An external asset is inaccessible to the renderer or a relative URL has no suitable base. Use embedded CSS where suitable; verify asset URLs are retrievable and resolve from the submitted document URL or markup context.
Rows split awkwardly Pagination constraints are missing, unsupported for the element, or impossible because the content exceeds the page. Apply break suggestions, keep individual rows reasonably sized, and inspect multi-page output. Do not expect an oversized row to remain whole.
QR code is absent or invalid The template has no QR image, the image cannot be fetched, or a placeholder/decorative code was used. For applicable e-invoicing, use the QR representation returned by the relevant process and ensure it is available to the renderer.
Invoice number/date rejected upstream The number is duplicated for the financial year or the date violates a relevant check. Validate uniqueness and date rules before rendering; the GST Portal manual flags future dates and dates before GST registration.
Invoice looks complete but lacks a conditional field A universal template was used without evaluating the transaction conditions. Model conditional particulars explicitly and have the template checked against the applicable Rule 46/54 requirements and current notifications.

9. Performance, reliability, and cost considerations

Keep invoice calculation and validation out of the rendering request where possible: render from a stable, validated record so retries do not silently change the invoice’s business data. A direct PDF call makes the user-facing operation wait for conversion; an asynchronous job or hosted-document flow can fit workflows that should return before a document is ready, but requires status and retrieval handling. The reviewed sources provide no basis for quoting render-time benchmarks or availability figures.

Reduce avoidable rendering dependencies by embedding modest stylesheets and using only necessary external assets. Check asset accessibility because a URL-based document can fail or render incompletely when linked resources are unavailable. Track response status and page-count headers for operational diagnostics, and distinguish API failures from valid PDF output.

DocRaptor’s test mode does not count against monthly limits but adds a watermark. The reviewed materials do not provide pricing figures, so check DocRaptor’s current pricing and account limits before estimating production cost. Also account separately for the application work and any upstream GST or e-invoice integration; a PDF renderer does not replace those responsibilities.

10. Or skip the browser setup

If you need a screenshot or visual capture of a page in your invoice workflow, ScreenshotNeo is a website screenshot API and MCP server for developers. It is separate from DocRaptor’s HTML-to-PDF flow and does not handle GST validation, invoicing, or e-invoice reporting. The API accepts one GET request with a URL and returns a PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed, and response headers identify the page verdict and billing status. An MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month with no card.

11. FAQ

Does DocRaptor decide whether a transaction is taxable?

No. Your application and qualified tax process must determine and validate the invoice data; DocRaptor renders the supplied content.

Can I use a URL instead of sending HTML?

Yes. The API supports document_url; ensure the document and its required assets are retrievable by the renderer.

Does a PDF with an IRN-looking QR code prove that e-invoice reporting is complete?

No. Complete the applicable GST portal or IRP workflow and use its returned data. A code drawn into a PDF is not a substitute for that process.

Can one invoice template serve every GST transaction?

Do not assume so. Rule 46 includes conditional particulars, and Rule 54 and transaction-specific requirements may also apply.

Can test-mode output be sent to a customer?

Test output carries a watermark. Use the appropriate non-test configuration for final invoice documents.

Sources