ArgoDoc

Documentation

Getting started

Three steps: authenticate, upload a template, send data to /generate. This page covers the whole request shape, including tables, images, and debug mode.

Before you start: install LibreOffice Writer. It's free, and it's what actually renders your finished documents on our servers. Design and save your template in LibreOffice Writer directly. Word can open and save the file, but it doesn't render every formatting detail the same way LibreOffice does, so a template saved from Word can come out spaced or styled differently once generated.

1. Authentication

Send your API key on every request:

X-API-Key: YOUR_API_KEY

That's the only credential you need for generating documents, managing templates, and checking usage. Get your key from your dashboard after signing up and verifying your email - keep it out of client-side code and version control, and rotate it any time if it's ever exposed.

Billing, key rotation, and account deletion require being logged into the website itself - not just an API key.

2. Design and upload a template

A template has two kinds of markers showing where your data goes:

<placeholder_name>

Replaced with a single text value or an image.

{table_name}

Marks a row to repeat once per item you send.

Upload it from the dashboard, or POST /templates as multipart form data with name and file fields.

3. Placeholder types

text

Replaces a placeholder with a string.

{
  "placeholder": "<client_name>",
  "type": "text",
  "value": "Acme Corp",
  "textFormatting": {
    "bold": true,
    "colour": "#7A2E2A"
  }
}
placeholderrequired

The tag in your template this replaces, e.g. <client_name>.

typerequired

Either "text" or "image".

valuerequired

The replacement string - up to 10,000 characters. Send an empty string to blank the placeholder out. Tabs and line breaks are fine; other control characters aren't.

textFormattingoptional

Overrides font, size (1–96pt), bold/italic/underline, text colour, and highlight colour for just this value, as #RRGGBB hex.

image

Replaces a placeholder with an embedded image. Width and height are in whole millimetres.

{
  "placeholder": "<logo>",
  "type": "image",
  "value": "iVBORw0KGgoAAAANSUhEUgAA...",
  "imageType": "png",
  "width": 40,
  "height": 20
}
placeholderrequired

Same as the text type - the tag this replaces.

typerequired

"image".

valuerequired

Base64-encoded PNG or JPEG data, up to ~5MB decoded.

widthrequired

Width in whole millimetres - 40 is 40mm (about 1.57in).

heightrequired

Height in whole millimetres.

imageTyperequired

One of "png", "jpeg", or "jpg" - must match the actual bytes you send.

An image inside a text box can't be replaced - keep it in the main body or a table cell.

4. Table tags

A placeholder swaps in a single value - but a table needs an entire row repeated once per item you send, and a placeholder has no way to say "repeat this". A tag on a row marks it as the one to duplicate, so the engine knows exactly which row to expand and how many times.

Each entry in tableRows becomes one repeated row. Every row has tableColumns, and each column can hold its own placeholders - matched to the template's columns left to right.

{
  "tag": "{items}",
  "tableRows": [
    {
      "tableColumns": [
        { "placeholders": [
          { "placeholder": "<item_name>", "type": "text", "value": "Widget" }
        ]},
        { "placeholders": [
          { "placeholder": "<item_price>", "type": "text", "value": "$10.00" }
        ]}
      ]
    }
  ]
}

A column can also hold its own nested tablePlaceholders, for a sub-table with its own tag inside a cell:

{
  "tag": "{items}",
  "tableRows": [
    {
      "tableColumns": [
        { "placeholders": [
          { "placeholder": "<item_name>", "type": "text", "value": "Widget" }
        ]},
        {
          "tablePlaceholders": [
            {
              "tag": "{sub_items}",
              "tableRows": [
                { "tableColumns": [
                  { "placeholders": [
                    { "placeholder": "<part_name>", "type": "text", "value": "Bolt" }
                  ]}
                ]}
              ]
            }
          ]
        }
      ]
    }
  ]
}

tableRows can also be an empty array. That's a normal, valid way to say "nothing to list this time" - the table renders with no rows, not as an error.

Limits: up to 2,000 items in any single array (rows, columns, placeholders, or nested tables), up to 5,000 total placeholders and rows in one request, up to 20,000 total table cells, and up to 5 levels of nested tables.

5. Debug mode & warnings

A missing or unusable placeholder or table tag never fails the request - you still get a 200 and a document back, with the literal text left in place wherever it couldn't be applied. The X-Docgen-Warnings header on every response is a quick signal something needs a closer look - it's a count of how many things were skipped, not a description of what happened.

For the actual detail, use debug mode: add ?debug=true to any /generate call and you get back a JSON report instead of the document - exactly which placeholders and table tags matched, which didn't, and the rendered document itself, base64-encoded, all in one response. It's the fastest way to debug a template that isn't behaving.

{
  "report": {
    "globalPlaceholders": [
      { "placeholder": "<client_name>", "replaced": 1 },
      { "placeholder": "<optional_note>", "replaced": 0 }
    ],
    "tablePlaceholders": [
      { "tag": "{items}", "replaced": 1, "tableRows": [ ... ] }
    ]
  },
  "warnings": [
    "Global placeholder <optional_note> was never replaced in the document"
  ],
  "document": "JVBERi0xLjQK..."
}

6. Account & usage

You've already seen POST /generate above - it's the core of the API. A few more endpoints round out template and account management, all callable with your API key. Click through them below to see exactly what each one sends back:

Request

GET https://undefined/usage

X-API-Key: YOUR_API_KEY

Response

200 OK

Content-Type: application/json
{
  "docs_used": 42,
  "docs_per_month": 500,
  "period_start": "2026-07-10T00:00:00Z",
  "period_end": "2026-08-09T00:00:00Z",
  "rate_limit_per_minute": 10
}

A complete request

One request combining a text value, an image, and a table with two rows - switch between scenarios below to see exactly what goes out and what comes back.

Request

POST https://undefined/generate

X-API-Key: YOUR_API_KEY
Content-Type: application/json
{
  "odtTemplate": "invoice",
  "outputType": "pdf",
  "globalPlaceholders": [
    {
      "placeholder": "<client_name>",
      "type": "text",
      "value": "Acme Corp"
    },
    {
      "placeholder": "<logo>",
      "type": "image",
      "value": "iVBORw0KGgoAAAANSUhEUgAA...",
      "imageType": "png",
      "width": 40,
      "height": 20
    }
  ],
  "tablePlaceholders": [
    {
      "tag": "{items}",
      "tableRows": [
        {
          "tableColumns": [
            {
              "placeholders": [
                {
                  "placeholder": "<item_name>",
                  "type": "text",
                  "value": "Widget"
                }
              ]
            },
            {
              "placeholders": [
                {
                  "placeholder": "<item_price>",
                  "type": "text",
                  "value": "$10.00"
                }
              ]
            }
          ]
        },
        {
          "tableColumns": [
            {
              "placeholders": [
                {
                  "placeholder": "<item_name>",
                  "type": "text",
                  "value": "Gadget"
                }
              ]
            },
            {
              "placeholders": [
                {
                  "placeholder": "<item_price>",
                  "type": "text",
                  "value": "$25.00"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Response

200 OK

Content-Type: application/pdf
Content-Disposition: attachment; filename="document.pdf"

The response body here is the finished PDF itself, as binary bytes - not shown.