Form endpoint anatomy: the URL, the answers, and the ceilings

One POST URL per project, two body formats, ten possible answers, and five limits. This is the whole contract, written from the code that enforces it.

The URL

Endpoint shape
POST https://www.submitharbor.com/api/submit/sh_your_project_key
     └────────────────────────────┘└──────────────────┘
              submission route        your project key
The key is public
It identifies the project, not your account. It is meant to sit in the HTML of a public page. It grants the ability to submit, never the ability to read — the inbox is reachable only with your signed-in credentials.
Keys are prefixed
sh_ for a project you created, demo_ for the shared demo. A key that does not match the expected shape is rejected with 404 without any lookup.
Two methods
POST to submit and OPTIONS for the CORS preflight. Anything else is not handled.

Accepted bodies

The endpoint reads the Content-Type and parses accordingly. There is no separate URL per format.

Content-TypeParsed asNotes
application/jsonA flat objectString and number values are read and trimmed. Nested objects and arrays are ignored, not stored.
application/x-www-form-urlencodedForm fieldsWhat a native HTML form sends by default.
multipart/form-dataForm fieldsText fields are read. File parts are not stored — there are no attachments.

CORS preflight

A JSON submission from a browser triggers a preflight. It is answered before any project lookup, so it is fast and never leaks whether a key exists.

OPTIONS response
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: POST, OPTIONS
Access-Control-Allow-Headers: Content-Type
Vary: Origin

Every answer the endpoint can give

Checks run in this order: key shape, project lookup, origin, rate limit, size, parse, honeypot, validation, storage.

ResponseMeaningWhen you get it
201 CreatedAcceptedThe submission passed validation. The JSON body reports whether it was stored and includes an X-RateLimit-Remaining header.
200 OKFilteredA honeypot field was populated. The response reports filtered: true and stored: false so a bot cannot tell it was caught.
303 See OtherRedirectedA stored submission included a _redirect target on the submitting origin or the project allow-list, so the browser is sent to your own thank-you page.
400 Bad RequestUnreadable bodyThe body was neither a JSON object nor ordinary form data.
403 ForbiddenOrigin not allowedThe project has an origin allow-list and the request came from a site that is not on it.
404 Not FoundUnknown endpointThe key is malformed, or no project exists behind a well-formed key.
413 Payload Too LargeBody too bigThe request declared more than 64 KB of content.
422 Unprocessable ContentValidation failedA field limit was exceeded, a field name was unusable, or email and message did not pass their checks. The error string is safe to show a visitor.
429 Too Many RequestsRate limitedMore than 20 submissions per minute from one address to one endpoint. A Retry-After header is included.
503 Service UnavailableStorage unavailableThe submission was valid but the configuration lookup or the write did not complete. Nothing was silently dropped.

Payload limits

LimitValueWhat happens at the edge
Request body64 KBLarger declared payloads are rejected with 413.
Fields per submission20Counted after control fields are removed; 422 beyond it.
Characters per field5,000Values are trimmed first, then checked; 422 beyond it.
Field name1–50 charactersLetters, numbers, underscores, and hyphens only.
Submissions per minute20 per address, per endpointA fixed 60-second window, then 429.

A successful submission returns an X-RateLimit-Remaining header, so a client can back off before it is refused rather than after.

Test it against the real route

Two ways to exercise the endpoint, both hitting the same code that serves production traffic.

Run this now — the shared demo key is open and answers immediately
curl -i -X POST https://www.submitharbor.com/api/submit/demo_contact_7x2p \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","message":"Testing the demo endpoint."}'

Or open the studio and use the test form on the setup screen. It posts to the endpoint over the network like any other client and shows you the captured payload that came back, including the origin and the timestamp.

The shared demo key validates a submission and echoes it back, then discards it. It never writes to the database, because a stored demo submission would leave a stranger's details in an inbox nobody owns. Create your own endpoint to keep what arrives.

What SubmitHarbor does not do

Worth reading before you build on it. These limits apply today.

  • The shared signed-out demo validates and echoes a preview; durable endpoints, storage, and notifications require a configured signed-in project.
  • SubmitHarbor does not promise an uptime SLA, attachments, CAPTCHA providers, webhooks, CRM integrations, or unlimited submissions.
  • Origin checks and honeypots reduce common abuse but do not replace a complete security and privacy review for sensitive forms.

Questions about form endpoint

Is the project key a secret?

No. It belongs in the HTML of a public page. It allows submissions to that one project and nothing else — reading the inbox requires your signed-in account.

What status code does a successful submission return?

201, with a JSON body reporting whether it was stored, which mode handled it, and how much of the rate-limit window is left.

Are there file uploads?

No. Multipart bodies are accepted and their text fields are read, but file parts are not stored. There are no attachments.