NEW AI agents now first-class: authorize · audit · revoke in one click — your agents submit cleanly, bots stay blocked. Read agent docs →

Get a form — or an agent inbox — working in 30 seconds.

One API for everything your agent sends, receives, and tests: headless forms, agent mailboxes (send, receive, reply, read OTP codes), and disposable test inboxes for CI. Forms are the fastest start — create one, copy the endpoint URL, point an HTML action= at it. Everything below is the long tail, including agent mail.

Start here — the 3 things every API integration needs
Base URL
https://login.ollastack.com — every /api/… path on this page is relative to it. Note: the bare ollastack.com is the marketing site; the API lives on login.ollastack.com.
Authentication
Authorization: Bearer <your-api-key> — create a key in Dashboard → Settings → API tokens (shown once at creation). Anonymous form submits to /api/submit/<slug> need no key.
Discover every endpoint
GET https://login.ollastack.com/api/openapi.json — the full machine-readable contract (OpenAPI 3.1, public + CORS-open). Its servers[] block names the canonical host, so an agent or MCP tool can self-configure from it.

All APIs, by what you're trying to do

One account, one token system. Every surface is published in the OpenAPI spec so agents can discover it. Pick your purpose:

I want to…API / endpointsGuide
Collect form submissions POST /api/submit/<slug> Quickstart ↓
Create & configure forms via API /api/forms, /api/forms/{id}/builder Forms API
Read / export submissions /api/submissions, /api/forms/{id}/export Exports ↓
Let an AI agent submit forms Bearer token, agent-tagged For agents →
Give an agent an email identity (chosen address, spam-filtered, send / receive / reply) /api/mailboxes/check-handle, /api/mailboxes, …/send, …/wait Agent Mail →
Test emails / OTPs in CI (disposable inbox) /api/mailboxes/{id}/wait Agent Mail →
Manage tokens & scopes /api/user/tokens API tokens ↓
Auto-discover the whole API (MCP / tool-use) GET /api/openapi.json OpenAPI ↓

Quickstart

  1. Sign up at login.ollastack.com/register and verify your email.
  2. Click Create form, name it, copy the endpoint URL — it looks like https://login.ollastack.com/api/submit/<slug>.
  3. Submit to it from anywhere. That's it.

HTML form

<form action="https://login.ollastack.com/api/submit/contact" method="POST">
  <input name="email" type="email" required>
  <textarea name="message" required></textarea>
  <input type="text" name="_gotcha" style="display:none">
  <button>Send</button>
</form>

_gotcha is the honeypot — leave it empty in your real form, bots will fill it.

React

A typed @ollastack/react hook (useForm) is publishing to npm soon. Until then the HTML and fetch/curl approaches need no install and work everywhere. Preview of the hook:

import { useForm, ValidationError } from "@ollastack/react";

export function Contact() {
  const [state, handleSubmit] = useForm("contact");
  if (state.succeeded) return <p>Thanks!</p>;

  return (
    <form onSubmit={handleSubmit}>
      <input name="email" type="email" required/>
      <ValidationError errors={state.errors} field="email"/>
      <button disabled={state.submitting}>Send</button>
    </form>
  );
}

Field errors come back as state.errors.fieldErrors — keyed by your form's field IDs.

fetch / curl

curl -X POST https://login.ollastack.com/api/submit/contact \
     -H "Content-Type: application/json" \
     -d '{"email":"ada@example.com","message":"hi"}'

JSON and application/x-www-form-urlencoded are both accepted. Success returns 201 Created with {"success":true,"id":"sub_…","next":<url|null>}.

Hosted form page

Don't want to build your own UI? Every form gets a Ollastack-hosted page at login.ollastack.com/f/<slug> — rendered server-side, with your form's title, fields, and (on Starter+) your custom CSS. Successful submits route to /thanks/<slug> or a redirect you configure.

Magic fields

Drop these special fields into your payload and we'll route or label things automatically.

fieldbehavior
_replyto / emailReply-To on the notification + autoresponder target.
_subjectOverrides the notification subject.
_nextOverride the success-redirect URL. Must be on an allowed origin or your form's host.
_gotchaHoneypot. Leave blank. Filled = bot.

_cc / _bcc are not read from public payloads — that would let anonymous senders use your form as an email relay. Configure default CC/BCC in the form settings instead.

Notification recipients

Each form sends its submission notifications to a set of recipient addresses — a primary notifyEmail plus optional CC and BCC, configured in the form's settings. Two rules apply:

planmax recipients (To + CC + BCC combined)
Free2
Paid4

Verified-email requirement. A form may only send notifications to an address that is verified on the account that owns the form. You must verify your own account email before you can set any notification recipient, and every recipient must be that verified address. This prevents a form from being pointed at a stranger's inbox (a mail-bomb vector) and protects sender deliverability. Setting an unverified or third-party address returns 400 VALIDATION_ERROR.

CAPTCHA

CAPTCHA protection has two layers. The provider is set once for the whole platform (Cloudflare Turnstile is the active provider). The per-form switchcaptchaRequired, toggled in the dashboard — decides whether a given form actually enforces it. With the switch off, a form skips CAPTCHA entirely even though a provider is configured.

When captchaRequired is on, every browser submission must carry a valid CAPTCHA token or it's rejected with 403 AUTHORIZATION_ERROR. Add the Turnstile widget to your form and pass its token via the X-Captcha-Token header or a _captcha field in the payload.

<form action="https://login.ollastack.com/api/submit/contact" method="POST">
  <input name="email" type="email" required>
  <textarea name="message" required></textarea>
  <!-- Turnstile renders a hidden input named cf-turnstile-response -->
  <div class="cf-turnstile" data-sitekey="0x4AAAAAADT1xrK0ChtVDEbe"></div>
  <button>Send</button>
</form>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

Turnstile writes a hidden cf-turnstile-response input into the form; on a normal POST it's submitted automatically and read as the _captcha token — no extra wiring needed. For fetch/JSON submits, read the token yourself and send it in the X-Captcha-Token header. The site key above is the public Ollastack Turnstile key; the secret key never leaves the server.

Allowed origins

Set allowedOrigins on the form (comma-separated list, e.g. https://example.com,https://www.example.com) to lock the endpoint to those origins. Browser requests from other origins get 403 ORIGIN_NOT_ALLOWED. Authorized agent requests follow a different origin model — see /docs/agents.

Schema validation

Every field you add in the form builder has a type and required/optional flag. The submit endpoint validates the payload before saving — missing required fields, wrong types, or out-of-range values come back as 400 VALIDATION_ERROR with error.fieldErrors mapping each field id to a list of messages.

Multi-step forms + show-if conditional logic

On Starter+, the form builder supports step-based layouts and conditional visibility: each field can have a showIf rule that references the value of another field (equals, not-equals, contains). Hidden fields are not validated and not stored on the submission.

Autoresponder

Toggle the autoresponder on the form's settings page and write a template with {{field_name}} placeholders. When a submission's payload contains a recognizable email field (_replyto, email, etc.), we send the rendered template back to that address automatically. Template variables are HTML-escaped — safe to interpolate user input.

Webhooks

Add one or more webhook URLs on the form's settings page. Every successful, non-spam submission POSTs the payload with an X-Ollastack-Signature header (HMAC-SHA256 of the raw body, hex). Failed deliveries retry with exponential backoff up to 5 attempts; the dashboard shows full delivery history per webhook. Toggle a webhook on/off without losing its config.

crypto
  .createHmac("sha256", secret)
  .update(rawBody)
  .digest("hex");
// === request.headers["x-ollastack-signature"]

Third-party integrations

Per-form, attach one or more of these. Each runs alongside webhooks (not instead of them).

typeconfig
slackIncoming-webhook URL (must be HTTPS). Posts a formatted submission card.
discordIncoming-webhook URL. Same payload format.
telegrambotToken + chatId. DMs or channel post.
mailchimpapiKey + audienceId + dataCenter (e.g. us19). Adds the email field to your list.

Need Gmail, Notion, Airtable, HubSpot, Sheets, or another 1,000+ destinations? Wire your webhook (above) into Zapier, Make, n8n, or Activepieces — they bring the destination, you bring the signed payload. Full walkthrough at /docs/integrations.

Exports

GET /api/forms/<formId>/export?format=csv (or json, excel). Optional from and to ISO dates filter by submitted-at. Returns a file attachment named after the form's slug. Same data is available paginated via GET /api/forms/<formId>/submissions.

Analytics

GET /api/forms/<formId>/analytics?days=30 returns {timeseries, spam, geo, days}: daily submission counts, spam-vs-total ratio, and country breakdown. The dashboard renders these as charts on each form's detail page.

API tokens + audit log

Create personal API tokens in Dashboard → Settings → API tokens. Tokens carry scopes (forms:read, forms:write, submissions:read, submissions:write) and are shown only once at creation. Use them as Authorization: Bearer <your-token>.

Every Bearer-authenticated API call writes a row to the audit log (method, path, status, hashed IP, user-agent, timestamp). Expand view activity on any token row to see its last 50 calls. Per-token slide-window rate limit is 300 req/min — exceed it and you get 429 RATE_LIMIT_EXCEEDED with retryAfter.

AI agents

Ollastack treats AI agents as first-class clients. Authorize an agent in Dashboard → Settings → API tokens, give it a label, and your agent can submit cleanly while bots stay blocked. Submissions are tagged in the inbox with the agent's label. Each agent is scoped to your account — there's no path to use it against another account's forms. Full walkthrough at /docs/agents.

Custom SMTP

Per organization, configure your own SMTP server in Dashboard → Orgs. Submission notifications, autoresponders, and digests route through your SMTP — emails arrive from your domain. Credentials are encrypted at rest; if your SMTP server fails, we transparently fall back to the platform sender so notifications never silently drop. Full walkthrough at /docs/smtp.

OpenAPI 3.1 spec

The full machine-readable contract lives at login.ollastack.com/api/openapi.json — public and CORS-open so agent frameworks can consume it directly. Both API token and session authentication schemes are documented, and every endpoint declares its error shape and required scopes.

Error envelope

Every error returns the same shape:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid input",
    "fieldErrors": { "email": ["Invalid email"] }, // optional
    "retryAfter": 60 // optional, seconds, on 429
  }
}
statuscodemeaning
400VALIDATION_ERRORfieldErrors maps field id → list of messages.
401AUTHENTICATION_ERRORSession missing/expired, or Bearer invalid.
403ORIGIN_NOT_ALLOWED / AUTHORIZATION_ERROROrigin not on the form's allowlist, or token missing required scope.
404NOT_FOUNDForm/submission/token doesn't exist or isn't owned by caller.
409CONFLICTe.g. registering an email that already exists.
429RATE_LIMIT_EXCEEDEDPer-IP, per-form, per-token, or org quota exceeded. retryAfter in seconds.
500INTERNAL_ERRORServer-side bug. We log and alert.

Rate limits

Ollastack has four independent rate limits. Each emits 429 RATE_LIMIT_EXCEEDED with a retryAfter seconds field.

scopeapplies totypical
Per-IPAnonymous submits + login + password-resettight (seconds)
Per-formSubmits to one form (any caller)tight (per minute)
Per-tokenAny Bearer-authed API call300 / minute
Per-org / monthCounted submissions (post-spam-filter)your plan's quota

Agents (Bearer-authed submits) skip the per-IP bucket — they typically share an outbound IP with the agent host — but per-form, per-token, and per-org quotas still apply.

Need something not documented? Email hello@ollastack.com or check the OpenAPI spec.