Documentation
Everything you need to collect, manage, and retrieve form submissions with Inbound.
Introduction
Inbound is a form backend. You point any HTML form (or fetch call) at a unique endpoint URL, and Inbound stores the submissions, filters spam, and emails you when new entries arrive — no server code required on your side.
Everything is organized in a simple hierarchy: a project groups related forms and holds shared settings like allowed domains and usage quotas, and each form has its own endpoint, notification settings, and API key.
Quick start
- 1Sign up and create a project from the dashboard.
- 2Create a form inside the project and copy its endpoint URL.
- 3Point your HTML form's
actionat that URL withmethod="POST". - 4Submit the form — the entry appears in your dashboard and you get an email notification.
<form action="https://inbound.drapi.dev/f/{form-id}" method="POST">
<!-- Honeypot (leave empty) -->
<input type="hidden" name="_honeypot" value="" style="display:none">
<input type="text" name="name" placeholder="Your name" required>
<input type="email" name="email" placeholder="Email" required>
<textarea name="message" placeholder="Message" required></textarea>
<button type="submit">Send</button>
</form>Creating a project
Projects are the top-level container. Each project has its own allowed domains, monthly submission quota, and analytics.
- 1Go to Projects in the dashboard sidebar.
- 2Click New project and give it a name (e.g. "Portfolio site").
- 3Open the project to add forms and configure allowed domains.
Allowed domains
Allowed domains restrict where submissions can come from. When a request hits your form endpoint, its Origin / Refererheaders are checked against the project's domain list. Requests from other origins are rejected with 403 DOMAIN_NOT_ALLOWED.
- 1Open your project and go to its Settings tab.
- 2Add each domain your forms live on, e.g.
example.com. - 3Subdomains are matched automatically —
blog.example.comis covered byexample.com.
localhost and 127.0.0.1 are always allowed, so local development keeps working.Creating a form
Each form gets a unique public ID and an endpoint URL of the shape https://inbound.drapi.dev/f/{form-id}.
- 1Inside a project, click New form and name it (e.g. "Contact form").
- 2Optionally configure email notifications and a redirect URL.
- 3Copy the endpoint URL from the form's Overview tab — integration examples for HTML, React, fetch, cURL and Axios are provided there.
Posting submissions
Send a POST request to your form endpoint. No authentication is needed — the request is validated by origin, rate limits, and spam checks. Accepted content types: application/json, application/x-www-form-urlencoded, and multipart/form-data.
curl -X POST 'https://inbound.drapi.dev/f/{form-id}' \
-H 'Content-Type: application/json' \
-d '{"name":"Jane Doe","email":"jane@example.com","message":"Hello!"}'All fields in the body are stored as the submission payload, with two rules:
| Field | Behavior |
|---|---|
_honeypot | Spam trap. Must be empty or absent — a non-empty value rejects the submission. Include it as a hidden field that real users never fill. |
_* | Any field starting with an underscore is treated as internal and stripped before the payload is stored. |
A successful submission returns:
{
"success": true,
"id": "665f1c2e8b3a4d0012ab34cd",
"redirectUrl": "https://yoursite.com/thank-you" // only if configured
}Fetching submissions
Besides accepting submissions, every form exposes a read API — useful for building an admin page on your own site that lists everything the form has received. Requests are authenticated with the form's API key, sent as a Bearer token.
curl 'https://inbound.drapi.dev/f/{form-id}/submissions?page=1&limit=25' \
-H 'Authorization: Bearer inb_your_api_key'Query parameters:
| Parameter | Default | Description |
|---|---|---|
page | 1 | Page number, newest submissions first. |
limit | 25 | Results per page, maximum 100. |
from | — | Only submissions created at or after this ISO date. |
to | — | Only submissions created at or before this ISO date. |
{
"form": { "id": "a1b2c3d4-...", "name": "Contact form" },
"submissions": [
{
"id": "665f1c2e8b3a4d0012ab34cd",
"data": { "name": "Jane Doe", "email": "jane@example.com", "message": "Hello!" },
"createdAt": "2026-07-08T09:30:00.000Z"
}
],
"pagination": { "page": 1, "limit": 25, "total": 1, "totalPages": 1 }
}Example: fetching submissions from your own server to render an admin page.
const res = await fetch('https://inbound.drapi.dev/f/{form-id}/submissions?limit=50', {
headers: { Authorization: `Bearer ${process.env.INBOUND_API_KEY}` },
})
const { submissions, pagination } = await res.json()API keys
The read API is protected by a secret per-form API key (prefixed inb_). The form's public ID appears in your website's HTML, so it can never grant read access on its own.
- 1Open your form and go to the Settings tab.
- 2In the API access card, click Generate API key.
- 3Copy the key and store it somewhere safe, e.g. an environment variable on your server.
From the same card you can regenerate the key (the old one stops working immediately) or revoke it to disable read access entirely.
Rolling the form ID
If your endpoint URL leaks or starts attracting spam, you can rotate the form's public ID. This generates a brand-new endpoint URL and permanently invalidates the old one — existing submissions are kept.
- 1Open your form's Settings tab.
- 2In the Form endpoint card, click Roll Form ID and confirm.
- 3Update every website that posts to the form with the new URL — the old URL returns
404immediately.
Email notifications
When enabled, every new submission triggers an email. Configure this in the form's Settings tab:
| Setting | Description |
|---|---|
| Recipient emails | Up to 2 addresses on the free plan. Defaults to your account email when empty. |
| Email subject | Custom subject line. Defaults to "New submission on [form name]". |
| Sender name | The name shown in the From field. |
| Redirect URL | Where the submitter is sent after a successful submission. Leave blank to return JSON. |
Pausing submissions
You can temporarily stop accepting submissions without deleting anything. While paused, the endpoint responds with 503 FORM_PAUSED and nothing is stored.
- 1Open the form's Settings tab and scroll to the danger zone.
- 2Toggle Pause submissions. Flip it back anytime to resume.
Rate limits & quotas
| Limit | Value | Scope |
|---|---|---|
| Posting submissions | 10 requests / minute | Per IP |
| Reading submissions | 60 requests / minute | Per IP |
| Payload size | 100 KB | Per submission |
| Monthly submissions | 100 / month (free plan) | Per project |
| Forms | 1 (free plan) | Per project |
When a rate limit is hit the API returns 429 with a Retry-After header. The monthly quota resets on the first day of each month (UTC).
Error reference
All errors share the same shape:
{ "error": "ERROR_CODE", "message": "Human-readable description." }Posting submissions:
| Code | HTTP | Meaning |
|---|---|---|
NOT_FOUND | 404 | Form ID is invalid or the form does not exist. |
PAYLOAD_TOO_LARGE | 413 | Body exceeds the 100 KB limit. |
RATE_LIMITED | 429 | More than 10 submissions per minute from one IP. |
FORM_PAUSED | 503 | The form is currently paused. |
DOMAIN_NOT_ALLOWED | 403 | Origin/Referer is not in the project’s allowed domains. |
SPAM_DETECTED | 400 | The honeypot field was filled in. |
QUOTA_EXCEEDED | 429 | The project’s monthly submission quota is used up. |
Fetching submissions:
| Code | HTTP | Meaning |
|---|---|---|
UNAUTHENTICATED | 401 | No Bearer token in the Authorization header. |
INVALID_API_KEY | 401 | The key is wrong or has been revoked. |
NOT_FOUND | 404 | Form ID is invalid or the form does not exist. |
RATE_LIMITED | 429 | More than 60 requests per minute from one IP. |