Web forms

The one endpoint your own site calls, and the only Wrap endpoint a browser can reach.

POST https://wrap.eel.software/api/inbound/web-form

Nearly all of Eel's API is server to server. This endpoint is the exception: it exists so your site's contact form can drop a conversation into the inbox.

It uses no API key. It identifies itself by the channel.

The body

{
  "channelInstanceId": "…",
  "email": "ana@example.com",
  "message": "I'd like a quote for 200 units",
  "name": "Ana Restrepo",
  "phone": "+57 300 123 4567",
  "formId": "home-contact",
  "source": "black-friday-landing",
  "consentMarketing": true
}
Field Required Notes
channelInstanceId Yes Identifies the form. Comes from the channel's tab in Wrap
email Yes It is what makes a reply possible. Max 254 characters
message Yes Between 1 and 20,000 characters
name No Up to 120
phone No Up to 40
formId No To tell two forms on the same channel apart
source No Where the visit came from
consentMarketing No Only an explicit true grants permission

On that last one: a missing field is not a refusal, and a false revokes nothing. Revoking has its own paths, and none of them is omitting a checkbox.

Signing submissions

If the channel has a secret configured, every submission must be signed. Compute an HMAC-SHA256 of the raw request body and send it as:

X-Wrap-Signature: sha256=<hex>

Sign the exact bytes you send, before any re-serialization. Turning the object back into JSON to sign it is the most common way to produce a signature that does not match.

Unsigned, or signed wrong, is rejected.

Without a secret

The endpoint accepts anonymous submissions, capped at 20 per minute per IP and per form.

That is a minimal net, not a defence. If the form sits on a public page, put a captcha in front of it on your own site.

Calls from the browser

It is the only Wrap endpoint that accepts them: it answers the preflight and sends Access-Control-Allow-Origin: *, so the form can be submitted from the visitor's own browser.

The rest of Wrap's public API sends no CORS headers and is unreachable from a page. An API key in a site's JavaScript is a published API key.

The channel decides the workspace

The workspace is read from the channel channelInstanceId points at. There is no workspace field in the body, so nobody can post an enquiry into somebody else's workspace by changing a value in the JSON.