Skip to content

API quick start

This page takes you from an empty account to a finished document made by your own code: an account, a template, a key and one HTTP request. Every field, answer and error is described in the document API reference.

Any AutomaDoc account can call the API: a Microsoft 365 organization, a Google Workspace domain, a Lark workspace or an email account. If the API is all you need, sign up with an email address: see API-only accounts.

Steps 2 and 3 take place in the Company Portal at company.automadoc.cc and need an admin of your account (in a Lark workspace, a Lark app admin).

A template is a Word, Excel or PowerPoint file with placeholders where your data goes. For this page, take a Word invoice with:

  • {Invoice_No} and {Customer} in the text,
  • a table row whose first cell starts with {#Items}{Product}, then {Qty}, and whose last cell ends with {Price}{/Items}: the row repeats once per item,
  • {Total | currency:"USD"} under the table.

The full syntax is in Template syntax.

To add it in the Company Portal:

  1. Open Templates and click New API template.
  2. Type a name and click Create API template.
  3. Open the menu at the end of the template’s row and choose Upload file. The file is checked as it is uploaded; if it has a problem, the page says so and you upload a corrected one.
  4. In the same menu, choose Copy template ID.

The API fills any template of your company, not only API templates: one made in the Excel add-in, from a Google Sheet or in the Lark Base extension works the same way, and its row on Templates has the same Copy template ID. When you sign in to the portal with Lark, make templates in the Base extension; its Document API card, on the Template tab, also shows the endpoint with the ID in it.

  1. In the Company Portal, open Company → API keys and click New API key.
  2. Fill in Key name with the system that will use it, for example ERP, and click Create key.
  3. Copy the key. It starts with amk_ and is shown once.

A company key opens every template of your company. Store it like a password. Revoke on the same page stops it at once.

A Lark Base template also has a key of its own, on its Document API card, which opens only that template.

Send the template’s data as JSON to POST /api/v1/templates/{templateId}/documents, with the key in the X-Webhook-API-Key header:

Terminal window
curl -X POST 'https://api.automadoc.cc/api/v1/templates/TEMPLATE_ID/documents?wait=20' \
-H 'X-Webhook-API-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"data": {
"Invoice_No": "INV-2026-001",
"Customer": "Acme Corp",
"Items": [
{ "Product": "Consulting", "Qty": 10, "Price": 120 },
{ "Product": "Travel", "Qty": 1, "Price": 340 }
],
"Total": 1540
},
"format": "pdf"
}'
  • data is the record. Each key fills the placeholder of the same name. Spaces, dots and other symbols in a key become _, so "Invoice No." fills {Invoice_No_}; case matters.
  • format is optional: "pdf", "png" (the first page as an image) or "native" (the template’s own kind of file). Left out, the template’s setting decides.
  • ?wait=20 holds the answer up to 20 seconds (25 at most) while the document is made, so one request is often enough.

filename, recordId and mode are optional too; see Create documents.

When the document is finished within the wait, the answer is 200 OK:

{
"generationId": "5d2f0c1e-6a1b-4c7e-9f0a-2b8c3d4e5f60",
"templateId": "9b1c…",
"status": "completed",
"statusUrl": "https://api.automadoc.cc/api/v1/documents/5d2f0c1e-6a1b-4c7e-9f0a-2b8c3d4e5f60",
"createdAt": 1791878400000,
"filename": "Invoice-INV-2026-001.pdf",
"downloadUrl": "https://api.automadoc.cc/api/generations/…/download?expires=…&signature=…",
"downloadUrlExpiresAt": 1792137600000
}

When it is still being made, the answer is 202 Accepted with "status": "processing" and no link yet. Read statusUrl with the same key every few seconds (at most 60 reads a minute per document) until status is completed or failed:

Terminal window
curl 'https://api.automadoc.cc/api/v1/documents/GENERATION_ID' \
-H 'X-Webhook-API-Key: YOUR_API_KEY'
  • completed: downloadUrl opens the file with no key. It works as long as your company keeps generated documents, and each status read gives a fresh one.
  • failed: error says why, for example a loop that is never closed.
  • warnings, when present, lists what was left out, such as a picture whose URL could not be fetched, or placeholders your data had no value for.

To get the file itself with the key instead of a link:

Terminal window
curl -o invoice.pdf 'https://api.automadoc.cc/api/v1/documents/GENERATION_ID/file' \
-H 'X-Webhook-API-Key: YOUR_API_KEY'

Every refusal has a code and an English error. The ones you meet first:

Status code What to do
401 API_KEY_MISSING Add the X-Webhook-API-Key header
403 API_KEY_INVALID Check the key and the template ID: a wrong ID gets this answer too
409 TEMPLATE_FILE_MISSING Upload the template’s file
400 TEMPLATE_VALIDATION_ERRORS Fix the template file; validationErrors lists the problems
403 QUOTA_EXCEEDED No documents left this month; buy a pack or a seat
429 RATE_LIMITED Over 60 requests a minute for one template, or 600 documents a minute for your company; wait and try again

The full list is in Errors.

Node.js 18 or later has fetch built in. Save this as quickstart.mjs and run it with AUTOMADOC_KEY=amk_… node quickstart.mjs:

import { writeFile } from 'node:fs/promises'
const API = 'https://api.automadoc.cc'
const KEY = process.env.AUTOMADOC_KEY
const TEMPLATE_ID = 'TEMPLATE_ID'
async function makeDocument(templateId, data) {
const res = await fetch(`${API}/api/v1/templates/${templateId}/documents?wait=20`, {
method: 'POST',
headers: { 'X-Webhook-API-Key': KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ data, format: 'pdf' }),
})
let doc = await res.json()
if (!res.ok) throw new Error(`${doc.code}: ${doc.error}`)
// Not finished within the wait: read the status until it is.
while (doc.status === 'processing') {
await new Promise((resolve) => setTimeout(resolve, 3000))
const poll = await fetch(doc.statusUrl, { headers: { 'X-Webhook-API-Key': KEY } })
doc = await poll.json()
if (!poll.ok) throw new Error(`${doc.code}: ${doc.error}`)
}
if (doc.status === 'failed') throw new Error(doc.error)
return doc
}
const doc = await makeDocument(TEMPLATE_ID, {
Invoice_No: 'INV-2026-001',
Customer: 'Acme Corp',
Items: [
{ Product: 'Consulting', Qty: 10, Price: 120 },
{ Product: 'Travel', Qty: 1, Price: 340 },
],
Total: 1540,
})
const file = await fetch(doc.downloadUrl)
await writeFile(doc.filename, Buffer.from(await file.arrayBuffer()))
console.log(`Saved ${doc.filename}`, doc.warnings ?? '')
  • Loops. A list of objects fills a {#Items}…{/Items} loop, one row or block per object; the loop can filter, sort and limit its rows. See Loops.
  • Pictures. Give a picture in the template the Alt Text {Logo} and send "Logo": "https://…". The URL must be a public http(s) address; anywhere else in the template a URL is just text. See Dynamic images and How your data fills the template.
  • PDF, image or Office file. "format" picks the output per request; the template’s own setting applies when you leave it out.
  • Many documents in one request. Send data as a list of up to 50 objects to get one document per item. See Several documents at once.
  • One document from many records. A template set to make a list, a page per record or labels turns a list of up to 500 items into one document, counted as one. See One document from many records and One document from all the items.
  • E-signature. For a Lark Base template with signature fields, a sign block sends the document out for signature as soon as it is made. See Send the document for signature.
  • No code at all. In Microsoft 365, the Power Automate connector makes the same request from a flow.