Skip to main content

A REST API built for systems integration

REST API for creating, polling, and fetching generated documents. Webhooks for async workflows. Idempotency keys, rate limits, and standard auth. Code examples in curl, Node, and Python.

If a team is using SourceToDocs at any scale, the API is how they’re actually using it. The web UI is for configuration — wiring a template to a data source, mapping fields, testing a generation. The API is for production: documents triggered by events in your CRM, scheduled from your orchestration layer, generated on-demand from an internal tool, dispatched from your own back-office system.

The API is included on every plan. There is no separate developer tier, no rate-limit difference between plans intended to push customers toward an upgrade.

What it does

The API exposes the platform’s generation pipeline as a small REST surface. The shape is conventional — anyone who has integrated against a modern SaaS API will recognize the patterns within a few minutes.

Authentication. API key plus workspace ID. Keys are scoped to a workspace, rotatable from the UI, and revocable instantly. We don’t use long-lived bearer tokens that expire; you control rotation.

Core resources. The primary resource is the generation — a single request to produce one or more documents from a template and a data binding. You create a generation, poll its status (or wait for a webhook), and fetch the resulting output files.

Async by default. Document generation is async — it takes anywhere from a few seconds for a short Slides deck to a minute or two for a long Word document with many conditional sections. The API reflects that reality: POST /generations returns immediately with a pending status, and the actual work happens in the background.

Idempotency. Every POST /generations accepts an Idempotency-Key header. Retrying a request with the same key returns the original generation instead of creating a new one. This means client retries on network failures are safe.

Webhooks. Configure a webhook URL per workspace; we send signed payloads when a generation completes or fails. Signatures use HMAC-SHA256 with a workspace-specific secret. Webhook retries follow exponential backoff for transient failures on your endpoint.

Rate limits. Per workspace, with the limit visible in response headers. Limits are sized to comfortably support production workflows; teams generating thousands of documents per day should reach out so we can scale appropriately.

How it works

A typical integration looks like this:

# Create a generation
curl -X POST https://api.sourcetodocs.com/v1/generations \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Workspace-Id: $WORKSPACE_ID" \
  -H "Idempotency-Key: account-42-qbr-q3-2026" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "tpl_abc123",
    "data_source_id": "src_xyz789",
    "filters": {"account_id": "42", "quarter": "2026-Q3"},
    "outputs": ["pdf", "google_slides"]
  }'

# Response — 202 Accepted
{
  "id": "gen_01HXXXXXX",
  "status": "pending",
  "created_at": "2026-05-13T10:00:00Z"
}

Then either poll for completion:

curl https://api.sourcetodocs.com/v1/generations/gen_01HXXXXXX \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Workspace-Id: $WORKSPACE_ID"

Or receive a webhook when the generation finishes:

{
  "event": "generation.completed",
  "generation_id": "gen_01HXXXXXX",
  "status": "completed",
  "outputs": [
    {"format": "pdf", "url": "https://..."},
    {"format": "google_slides", "url": "https://docs.google.com/..."}
  ]
}

The output URLs are signed and time-limited — fetch the file, store it where you need it, or redirect users to it directly.

What’s included

  • REST API with predictable resource semantics. No surprise endpoint behaviors, no auth quirks.
  • Webhooks for both success and failure events. Signed payloads, retries, full request log in the UI.
  • SDK-friendly response shapes. Consistent JSON envelopes, errors that include both human-readable messages and machine-codable error types.
  • Idempotency on every state-changing endpoint.
  • Detailed request logs in the UI for the last 30 days. Useful when an integration starts misbehaving in production.
  • Rate-limit headers on every response: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.
  • Versioned API. The current version is reflected in the URL path. Breaking changes will land in a new version; the previous version stays available for a defined deprecation window.

See pricing →

Code examples in three languages

Node (using fetch):

const res = await fetch('https://api.sourcetodocs.com/v1/generations', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.S2D_API_KEY}`,
    'X-Workspace-Id': process.env.S2D_WORKSPACE_ID,
    'Idempotency-Key': `qbr-${accountId}-${quarter}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    template_id: 'tpl_abc123',
    data_source_id: 'src_xyz789',
    filters: { account_id: accountId, quarter },
    outputs: ['pdf'],
  }),
});

const generation = await res.json();
console.log(generation.id); // gen_01HXXXXXX

Python (using requests):

import os
import requests

response = requests.post(
    "https://api.sourcetodocs.com/v1/generations",
    headers={
        "Authorization": f"Bearer {os.environ['S2D_API_KEY']}",
        "X-Workspace-Id": os.environ["S2D_WORKSPACE_ID"],
        "Idempotency-Key": f"qbr-{account_id}-{quarter}",
        "Content-Type": "application/json",
    },
    json={
        "template_id": "tpl_abc123",
        "data_source_id": "src_xyz789",
        "filters": {"account_id": account_id, "quarter": quarter},
        "outputs": ["pdf"],
    },
)

generation = response.json()
print(generation["id"])  # gen_01HXXXXXX

The patterns are intentionally boring. There’s nothing to learn beyond the resource shapes.

Where it fits

Triggering generation from CRM workflows. A Salesforce flow that fires when an opportunity moves to a particular stage, calling the SourceToDocs API to produce a tailored proposal. The CSM doesn’t have to leave their CRM; the document lands in their browser tab when it’s ready.

Scheduled generation from your own orchestration. Teams who already run cron jobs, Airflow DAGs, or n8n workflows can call the API from their existing scheduler instead of using ours. The generation logic is just another step in the pipeline.

Embedding into internal tools. A Retool, Forest, or custom-built admin UI where operators trigger generations as part of a broader workflow. The API integrates with whatever tool you already have rather than asking you to context-switch.

Server-side batch generation. Producing hundreds or thousands of documents in a batch (a customer migration, an annual report run, a one-time backfill). Idempotency keys make retries safe; webhooks make completion-tracking straightforward.

Honest limits

  • No public sandbox environment yet. Testing happens in your workspace against your own test templates and test data. Most teams use a separate “test” workspace for integration development.
  • REST only. No GraphQL surface. No gRPC. If you need a different transport, the REST API is what we offer today.
  • No streaming progress events. You get binary done/not-done via webhook or polling. We don’t currently emit incremental progress signals for long-running generations.
  • No file-content streaming on fetch. Generated outputs are returned as signed URLs to download. We don’t stream file bytes through the API itself.
  • Webhook delivery is best-effort with retries. We retry failed deliveries on a backoff schedule, but webhook delivery is not a guaranteed-once-and-only-once contract. If your downstream system requires that, dedupe by generation ID on your end.

FAQ

Is there a CLI?

Not officially, today. The API is straightforward enough to script against in any language; we haven’t built a wrapper CLI because users tend to embed the API into their own automation rather than calling it interactively.

Can I generate from raw data instead of a connected source?

Yes. Instead of pointing to a data_source_id, you can include a data object in the request body with the records inline. Useful for one-off generations where wiring a persistent source isn’t worth the setup.

How do I handle generation failures?

Failure webhooks include a structured error object with a code, a message, and where possible the specific placeholder or field that caused the failure. Common failure modes (missing required field, unreachable data source, invalid template format) all return useful error codes. Less common modes return a generic code plus a debugging context object.

What about retention of generated files?

Generated files are retained for 90 days by default. After that, the metadata stays (you can see the generation happened, what it produced) but the output files are pruned. Workspaces that need longer retention can configure it; reach out.

Can I revoke a single API key without disrupting others?

Yes. Keys are independent and rotatable. Common practice is one key per integration — your CRM workflow uses a different key from your batch generation script — so rotating one doesn’t disrupt the others.

Is the API documentation public?

A full reference is available in your workspace once you log in.

Related reading