Montlake Labs
Developers

API Reference

REST endpoints, webhook payloads, and SDK helpers. Everything is versioned, paginated, and rate-limited the way you'd hope it is.

Authentication

Every request needs a bearer token. Tokens are workspace-scoped, can be rotated from the dashboard, and inherit role-based permissions from whoever created them.

cURL
curl https://api.montlakelabs.test/v1/flows \
  -H "Authorization: Bearer mlk_live_…" \
  -H "Accept: application/json"
Node SDK
import Montlake from "@montlakelabs/sdk";

const mlk = new Montlake(process.env.MLK_KEY);

const flows = await mlk.flows.list({ limit: 25 });

Versioning

The current major version is v1. Breaking changes ship under a new major (v2, v3) and the previous major is supported for at least 24 months after a new one is released. Additive changes — new fields, new endpoints, new optional parameters — ship inside the current major without a version bump.

Rate limits

Limits are per workspace, not per token, so spinning up a second key doesn't double your budget. Every response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. When you exceed the limit you get a 429 with a Retry-After header.

PlanRead req / minWrite req / min
Basic / Pro / Premium12060
Team300120
Business600240
Enterprise1,200600

Errors

All errors return a JSON body with a stable code, a human-readable message, and a request_id you can quote when contacting support.

{
  "code": "flow_not_found",
  "message": "No flow with id flw_01HZA… in this workspace.",
  "request_id": "req_01JC4W7Q8N…"
}
StatusMeaning
400Validation failed — fix the request body.
401Token missing, expired, or revoked.
403Token valid but lacks the required role.
404Resource not found in this workspace.
409Conflict — e.g. duplicate slug or stale version.
429Rate-limited. Honor Retry-After.
5xxOur problem. Safe to retry with backoff.
GET /v1/flows

List all flows in the current workspace.

Paginated with cursor and limit (max 100). Filter by status=active, tag, or created_after.

curl "https://api.montlakelabs.test/v1/flows?limit=25&status=active" \
  -H "Authorization: Bearer mlk_live_…"
GET /v1/flows/{id}

Retrieve a single flow with its full step graph.

Returns the published version by default. Pass ?version=draft to fetch the in-progress draft instead.

POST /v1/flows/{id}/trigger

Manually trigger a flow with an inline payload.

The JSON body is passed to the flow as the trigger input. Returns a run_id you can poll or stream.

curl -X POST https://api.montlakelabs.test/v1/flows/flw_01HZA…/trigger \
  -H "Authorization: Bearer mlk_live_…" \
  -H "Content-Type: application/json" \
  -d '{"order_id": "ord_99421", "amount": 4200}'
PATCH /v1/flows/{id}

Update a flow's metadata or active state.

Only name, tags, and status are PATCH-able from the API. The step graph itself is versioned and edited from the visual builder.

GET /v1/runs

List recent runs across all flows in the workspace.

Filter by flow_id, status (queued, running, succeeded, failed, cancelled), or correlation_id.

GET /v1/runs/{id}

Retrieve a single run with every step timed and stamped.

Each step in the response includes its input, output, duration in milliseconds, and any retry attempts. Sensitive fields can be redacted via the workspace's data-handling rules.

POST /v1/runs/{id}/replay

Replay a single step or the entire run from where it stopped.

Pass step_id to replay one step, or omit it to replay every step after the first failure. Replays create a new run with a reference back to the original.

DELETE /v1/runs/{id}

Cancel a queued or in-flight run.

Cancellation is best-effort — steps already in transit to a third-party API may still complete. The run record reflects the final state including any partial side effects.

GET /v1/connections

List all third-party connections in the workspace.

Returns the provider, the connection's label, the OAuth scopes granted, and the user who created it. Secret material is never returned by the API.

POST /v1/connections

Create a connection from an OAuth callback or an API key.

For OAuth providers, exchange the authorization code yourself and post the resulting tokens. For API-key providers, post the key alongside the provider slug. We encrypt at rest and expose only an opaque connection ID to flows.

PATCH /v1/connections/{id}/rotate

Rotate the token backing a connection in place.

Flows using the connection are unaffected — the connection ID stays the same, only the secret behind it changes. Useful for incident response or scheduled rotation.

DELETE /v1/connections/{id}

Revoke an OAuth connection. Flows using it will pause.

Deletion is immediate and non-reversible. Any flow that referenced this connection enters a paused: missing_connection state until you wire it to a replacement.

Webhook subscriptions

Subscribe a URL to one or more event types — run.succeeded, run.failed, flow.published, connection.revoked. Subscriptions are scoped to the workspace and authenticated via the signature scheme below.

curl -X POST https://api.montlakelabs.test/v1/webhooks \
  -H "Authorization: Bearer mlk_live_…" \
  -d '{
    "url": "https://example.com/hooks/mlk",
    "events": ["run.succeeded", "run.failed"]
  }'

Webhook signatures

Every outbound webhook is signed with HMAC-SHA256 using your endpoint's secret. Verify before you trust.

// Node
import crypto from "node:crypto";

function verify(rawBody, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

Retry semantics

A delivery is considered successful when your endpoint returns a 2xx within 10 seconds. Anything else — non-2xx, timeout, connection refused — triggers a retry. We retry up to 8 times with exponential backoff (5s, 30s, 2m, 10m, 30m, 2h, 6h, 24h). After the final failed attempt the delivery is marked dead_lettered and the subscription continues running.