Search⌘K

Getting Started with the ZiplineOS API

A getting-started guide for developers integrating with the ZiplineOS API — authentication, your first request, and conventions.

This guide is for developers integrating with ZiplineOS — whether you're building a portal screen against the same API the product uses, or wiring an external system into a ZiplineOS organisation. It covers which auth model to pick, your first request, base URLs, error shapes, and rate limits.

ZiplineOS is a single ASP.NET 8 Minimal API service on Cloud Run, fronting a multi-tenant data model. Everything here is versioned under /api/v1/....

Pick your auth model first

The single most important decision is which authentication model your integration uses — it determines the base URL, the headers, and the rate-limit tier.

SurfaceWho it's forCredentialTenant scoping
Session cookieFirst-party portals acting as a signed-in userFirebase-issued __session cookieX-Organization-Id header
Programmatic API tokenServer-to-server integrations on a published Engine / webhook endpointX-Api-Key: zpl_… static token, or Authorization: BearerResolved from the token's organisation
OAuth2 client credentialsIntegrations that prefer a short-lived JWT over a static keyAuthorization: Bearer <jwt> from POST /api/v1/oauth/tokenResolved from the credentials
Anonymous resource tokenPublic client-portal flows (Collect, magic links, Preference Centre)A per-resource cryptographic token in the URLImplicit — the token names one resource

To choose the right model:

  1. Building a first-party portal screen? Use the session cookie model — it's what every authenticated screen already uses.
  2. Integrating an external service without a logged-in human? Use a programmatic API token or OAuth2 client credentials.
  3. Serving a public end-user flow via a one-off link? Use an anonymous resource token embedded in the URL.

Pick the row that matches your integration and skip the others.

Base URL

All endpoints live under /api/v1/.... The production base URL is:

https://api.ziplineos.com.au

A few path conventions worth knowing:

  • Inbound webhooks land under /api/v1/webhooks/... and use each provider's own auth check (for example, HMAC for Mailgun) rather than the cookie flow.
  • Published Engine endpoints live under /api/v1/logic/webhook/{slug}.

Your first request

The Firebase-backed session cookie is issued by POST /api/v1/auth/session; browser code typically already has it. Send the cookie and set the org id on every call:

const res = await fetch("https://api.ziplineos.com.au/api/v1/contacts", {
  credentials: "include",                 // sends the __session cookie
  headers: {
    "X-Organization-Id": activeOrgId,     // which tenant you're acting as
  },
});

A request that authenticates but omits X-Organization-Id on a tenant-scoped endpoint is a hard 400. If your call drops credentials: "include", you'll get 401 on every request. These are the two most common first-day mistakes.

You can confirm which org ids you're allowed to use via GET /api/v1/users/me/organizations.

B. Programmatic API-token request (external integrations)

ZiplineOS issues scoped API tokens prefixed zpl_. These are managed on the API Tokens page in the Protocol portal and target published Engine endpoints. Both of the following headers are accepted:

# Using the X-Api-Key header
curl -X POST "https://api.ziplineos.com.au/api/v1/logic/webhook/<slug>" \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: zpl_YOUR_TOKEN" \
  -d '{"inputs": {"loanAmount": 50000}}'
# Or the standard Authorization: Bearer header — equivalent for api_key tokens
curl -X POST "https://api.ziplineos.com.au/api/v1/logic/webhook/<slug>" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer zpl_YOUR_TOKEN" \
  -d '{"inputs": {"loanAmount": 50000}}'

A missing token returns 401 with "Missing API token. Provide via X-Api-Key or Authorization: Bearer header."

C. OAuth2 client-credentials (short-lived JWT)

Some Engine endpoints use an OAuth2 client-credentials flow. Exchange a Client ID + Client Secret for a JWT, then use it as a bearer token:

# 1. Exchange credentials for a JWT (valid ~24 hours)
curl -X POST "https://api.ziplineos.com.au/api/v1/oauth/token" \
  -H "Content-Type: application/json" \
  -d '{"grantType": "client_credentials", "clientId": "...", "clientSecret": "..."}'

# 2. Call the engine with the returned JWT
curl -X POST "https://api.ziplineos.com.au/api/v1/logic/webhook/<slug>" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_JWT_FROM_OAUTH_TOKEN" \
  -d '{"inputs": {"loanAmount": 50000}}'

The JWT is checked for a per-engine scope of the form engine:<slug>:execute. Client credentials are managed on the API Tokens page in the Protocol portal alongside static tokens.

Working with API tokens

PropertyBehaviour
PrefixEvery token starts with zpl_ for easy identification in logs and config.
Shown onceThe raw token is returned only at creation time; the server stores a SHA-256 hash. If you lose it, rotate — you can't recover.
ScopedEach token is granted specific scopes (e.g. contacts:read, deals:write) rather than full access.
ExpiryTokens carry an expiry between 1 and 365 days.
RevocableTokens can be revoked; a revoked token stops working within about a minute (~60s cache).

Grant the narrowest scopes your integration needs, set a realistic expiry, and store the raw zpl_… value as a secret. Tokens are only ever shown once.

Response and error shapes

ZiplineOS uses conventional HTTP status codes. Common ones for integrators:

CodeMeaning
200 / 201 / 202Success — 201 for creates, 202 for async work enqueued
400Validation failure or missing X-Organization-Id
401Missing/invalid session cookie or token
403Authenticated, but lacking the required role/membership
404Not found, or not visible under your org scope
429Rate limit exceeded
500Unhandled exception — body includes a traceId

Validation errors come back as 400, not 500. The body has a top-level error string (set to the first validation message) plus an errors map of field → string[]:

{
  "error": "Name is required.",
  "errors": {
    "name": ["Name is required."],
    "loanAmount": ["Must be greater than 0."]
  }
}

Use the errors map for field-level handling — don't pattern-match on the top-level error string.

Rate limits

TierLimitApplies to
Global1200 req/minAll traffic (per IP)
ApiStandard300 req/minStandard authenticated API calls (per user)
WebhookApi1000 req/minEngine endpoints with X-Api-Key (per API key)
AuthStrict20 req/minLogin / session endpoints (per IP)

On 429, back off and retry — the sliding window restores capacity quickly. A 429 can also come from the edge WAF before reaching the application; if your 429s don't correlate with the budgets above, the edge may be the source.

Quick checklist

  1. Confirm your auth model — cookie, programmatic token, OAuth2 JWT, or anonymous resource token.
  2. Use the production base URL https://api.ziplineos.com.au, all paths under /api/v1/....
  3. Send the right headers every call — cookie code needs credentials: "include" + X-Organization-Id; token code needs X-Api-Key or Authorization: Bearer.
  4. Handle the standard error shapes — especially 400 validation bodies (error + errors map) and 401/403/429.
  5. Respect rate limits — back off on 429; scope and expire tokens tightly.