Docs

Ask a person from your agent.

Your agent describes what it needs (a few fields, or a yes or no) and gets back a link. The person opens it on their phone, with no sign-up, and their answer comes back to you as validated JSON.

  1. Create a request. Call createRequest with a JSON Schema for a form, or a reason for an approval.
  2. Send the link. Over the channel you already have: email, chat or SMS.
  3. Get the answer. It arrives at your webhook, or read it with getStatus.

The same tools are available two ways: an MCP server for agents that speak MCP, and a REST API for everything else. Agents can read this guide in llms.txt.

Quickstart

From an API key to a person's answer in four steps.

1Get an API key

Sign in with your email and copy the key from the dashboard. It starts with sk_live_; keep it on your server. Using Claude Code, Codex or another MCP client? Skip this: add the server by URL and sign in from the client.

2Create a request

This one asks Jordan for a shipping address and a delivery date. The schema is plain JSON Schema; see Form schema for what's supported.

3Send the link

The response has one link per person in links[] (or a single url for an open link). Send it however you like; Run it by doesn't message people for you, except reminders.

4Get the answer

When Jordan submits, your webhookUrl receives a response.created event with the data, already checked against your schema. No webhook? Call getStatus with the requestId.

Authentication

Every MCP and REST call sends an API key as a bearer token. The same key works for both. A missing or revoked key gets a 401. There are two ways to get one.

Sign in from your MCP client

Add https://runitby.app/mcp with no key and the client signs you in over OAuth: a browser opens, you enter your email, and the sign-in link brings you back to a page that asks you to allow the app. First time? The same link creates your account. The app then holds its own key, named after it, which you can revoke under Keys.

  • Claude Code: run the command on the right, then /mcp in Claude Code and choose to authenticate.
  • Codex: run codex mcp add, then codex mcp login.
  • Claude (web and desktop): Settings, Connectors, Add custom connector, and paste the URL.
  • Other clients that support MCP authorization: add the URL with no headers.

Create a key in the dashboard

For the REST API, scripts, and clients that only take a header: create and revoke keys in the dashboard and send the key in the Authorization header.

Plans count the two apart. Free includes one key and one connected app, Builder five of each, Pro as many as you need.

Tools and endpoints

Each MCP tool has a REST endpoint with the same input and output. The API reference lists every field.

MCP tool REST What it does
createRequest POST /requests Create a form or approval and get the link(s) to send.
previewRequest POST /requests/preview Screenshots and lint of what the person will see. Creates nothing.
getStatus GET /requests/{id} Status, verdict and every answer so far.
listRequests GET /requests Your requests, newest first. Filter by mode or status; page with cursor.
updateRequest PATCH /requests/{id} Change content, add recipients or set a new expiry.
remind POST /requests/{id}/remind Email the link again to people who haven't answered.
closeRequest DELETE /requests/{id} Stop accepting answers.

Forms and approvals

Every request has a mode:

"form"
Collect data. You send a schema; the person gets a form built from it, and you get back an object that matches it.
"approval"
Get a decision. You send approval.reason; each person gets a card with Approve and Decline (or your own labels) and an optional comment. You get each decision and an overall verdict.

Both take a title (up to 120 characters), an optional requester line saying who's asking, a locale for the page ("en" or "es"), and metadata that comes back untouched in every webhook and status.

Who answers

audience decides who gets a link.

kind You get Use it for
"named" links[], one per person in recipients (1 to 50, each { name, email?, phone? }) Answers you need to attribute to someone. Approvals must be named. An email enables reminders and email codes.
"open" One url anyone can open Sign-ups and surveys. maxSubmissions caps the answers (default 1).

What to use it for

A mode and an audience cover most things you'd otherwise do with a form builder, a poll, a scheduling app or an email thread.

You want Request
One person's details: a shipping address, client intake, a booking slot "form", named, one recipient
A survey, poll, feedback or sign-ups from anyone "form", open with maxSubmissions. Post the one url anywhere; getStatus returns every answer for your agent to summarise.
RSVPs, availability, shift cover or check-ins from a known group "form", named (up to 50), so every answer is attributed and remind chases the rest
Approvals, sign-offs and votes "approval" with a policy; add verify: "email" for money, access or deletion

Status and expiry

status Means
pending Nobody has answered yet.
partial Some named people have answered, others haven't.
complete Everyone answered, maxSubmissions was reached, or an approval has its verdict.
expired The link ran out of time.
closed You called closeRequest.

Expiry. expiresIn takes "30m", "48h", "7d" or seconds. The default is 7 days and the maximum 90.

Changing a request. updateRequest changes the title, schema, sections, reason or theme until the first answer arrives. Adding recipients and setting a new expiry (counted from now) work until the request is complete, expired or closed.

Reminders. remind emails the link again to named people who haven't answered and have an email, at most once every 24 hours each.

Form schema

Plain JSON Schema with type: "object" at the top and up to 40 fields. required[] is enforced, and title and description become the label and help text.

Type Keywords
string format (email, date, time, date-time, tel, uri, duration), minLength, maxLength, pattern
number, integer minimum, maximum
boolean const: true makes a consent box that must be ticked
enum A fixed list of choices
array Of primitives or of an enum; minItems, maxItems

Hints. Put per-field options in "x-runitby": widget, placeholder, help, columns (1 or 2), step and currency.

Sections. Group fields with sections: [{ title, fields: [...] }] next to the schema.

Nested objects (except the location field), oneOf, anyOf, allOf and $ref are rejected with a message saying what to change. Fields asking for passwords, PINs, card numbers, CVVs, security codes, SSNs or one-time codes are refused.

Field types

Ready-made fields for things people find awkward to type. Each one is ordinary JSON Schema plus a widget, so the answer validates like any other field.

Field Schema Answer
Date string, format date "2026-11-12"
Time string, format time; step = minutes (1–60) "14:30"
Date and time string, format date-time "2026-11-12T14:30:00-08:00"
Duration string, format duration "PT1H30M"
Time slots array of date-time enum, widget slots the picked times
Date range array of 2 dates, widget daterange ["2026-11-12", "2026-11-15"]
Location object, widget location { latitude, longitude, accuracy }
Money number, currency "USD" 1200
Slider, rating number or integer; slider needs minimum and maximum 7
Chips, radio, select enum (chips also takes an array of enum) "M"
Rank array of enum, widget rank every option, in the person's order
Yes or no boolean, widget yesno true
Colour string, widget color "#C2410C"
Country string, widget country "US" (ISO 3166-1)
Language string, widget language "es-AR" (BCP 47)
Time zone string, widget timezone "America/New_York"

Add an enum to a country or language field to limit the choices.

Preview

previewRequest takes the same form or approval fields as createRequest (no audience, expiry, webhook or metadata) and returns screenshots at 390 px and 1280 px wide, plus lint[] with anything that will read badly. Nothing is created.

Pass sampleData to see the form filled in, and viewports to get only one size. Preview first when the same form will be sent many times.

Themes

theme makes the page look like your product. Without it, pages are ink on warm white, Figtree with Fraunces headings, round corners.

Key Values
accent, background, surface, text Hex colours
font, headingFont system, Figtree, Geist, Inter, DM Sans, Manrope, Work Sans, Space Grotesk, IBM Plex Sans, Fraunces, Lora, Source Serif 4
radius sharp, soft, round
density compact, comfortable
layout stacked (all fields on one page) or single (one question at a time)
logoUrl Your logo
heroText, thankYou The greeting above the form and the message after sending

Approval options

Approvals need a named audience: each person gets their own link, so every decision is attributed to them. The verdict is "approved", "declined", or "expired" when nobody decided in time; never a silent yes.

approval field Default Notes
reasonrequired What you're asking for, up to 300 characters.
detail Context under the reason, up to 1,000 characters.
policy "all" "any", "all", "majority" or { n }: how many approvals it takes.
options Approve, Decline Two to four labels; the first counts as approval. Decisions come back in snake_case: "Approve with changes" → "approve_with_changes".
allowComment true Let people add a comment to their decision.
showOthers false "names" or "counts" shows each person where the others stand.
verify false "email": each person confirms a 6-digit code sent to their own address before the buttons work, so a forwarded link can't decide for them. Every recipient needs an email. Use it for money, access or deletion.

Webhooks

Set webhookUrl (https) on a request and we POST JSON to it for two events:

response.created
Each submission or decision, with the answer in response.
request.completed
Every named person answered, maxSubmissions was reached, an approval has its verdict, or the request expired.

Verify the signature. X-Runitby-Signature is the hex HMAC-SHA256 of the raw body with your account's signing secret, from the dashboard. X-Runitby-Timestamp is the send time in Unix seconds.

Retries. Any 2xx within 10 seconds counts as delivered. Otherwise we retry 4 times, 1 min, 5 min, 30 min and 2 h after each failure. After that the answer is still there in getStatus.

Errors

Errors are JSON with a readable message and, for invalid input, problems[] pointing at each field to change. Agents can usually fix the request from the message alone.

Status When
400 The input or schema isn't valid.
401 Missing or revoked API key.
404 No request with that id on your account.
409 The change isn't allowed any more, e.g. editing after the first answer or reminding a closed request.
429 Over 60 creations a minute, or the plan's monthly requests are used up.

Plans and limits

Free Builder Pro
Requests a month 100 1,000 Unlimited
Answers per open link 25 10,000 Unlimited
API keys 1 5 Unlimited
Connected apps (OAuth) 1 5 Unlimited
Answers kept after delivery 30 days 90 days Kept

Past the monthly limit createRequest returns 429 until the period resets or you upgrade; links already sent keep working. Without a webhook, answers are kept from submission instead. See pricing.

On every plan: 60 creations a minute per API key, title up to 120 characters, approval.reason 300, approval.detail 1,000 and metadata under 4 KB.

You are the data controller for what you collect. Ask only for what the task needs.