# HTTP endpoints

> The public HTTP endpoints Conclude exposes for web forms and inbound email, with request and response shapes from the code.

Canonical HTML page: https://conclude.io/doc/reference/endpoints/
Last updated: 2026-10-27

---

This page lists every public HTTP endpoint served under `/v1` on `api.conclude.io`, for developers wiring a website or mail provider to an app. [Automation](https://conclude.io/doc/apps/automation/) inside Conclude does not use these endpoints; it uses [YAML](https://conclude.io/doc/reference/yaml/), [triggers](https://conclude.io/doc/reference/triggers/), [scripts](https://conclude.io/doc/reference/scripting/), [connectors](https://conclude.io/doc/reference/connectors/), the [lookup endpoint](https://conclude.io/doc/reference/connectors/#lookup-endpoint) and [agents](https://conclude.io/doc/reference/agents/). To work with connections, channels and the audit log from your own systems, use the [API](https://conclude.io/doc/api/).

## Summary

| Method and path | Called by | Purpose |
|---|---|---|
| `POST /v1/form/<cid>` | Your web page | Create a ticket from a form submission |
| `POST /v1/activity.form/<cid>` | Older embed scripts | Same handler as `/v1/form/<cid>`, kept for compatibility |
| `POST /v1/email/<id>` | The inbound mail provider | Deliver an email to an email connector |

`<cid>` and `<id>` are connector IDs ([web connector reference](https://conclude.io/doc/reference/connectors/#web-connector)). Every endpoint applies a per-client rate limit (IP address plus user agent) first and answers `429` when it is exceeded.

## POST /v1/form/\<cid\>

Creates a ticket in the channel where the app owning connector `<cid>` is installed; the [web form guide](https://conclude.io/doc/apps/integrations/web-form/) covers the page side.

**Request.** `Content-Type` is `application/json` (a flat object of strings, up to 16 MB) or `application/x-www-form-urlencoded` (first value per field); anything else is `400`. The connector ID is read from a `cid` header, a `cid` body field or the URL path, in that order; without one the response is `404`. Fields map to attributes through the connector's `attributeMap` ([syntax](https://conclude.io/doc/reference/connectors/#attribute-map-syntax); the keys are `external` and `internal`); unmapped fields keep their names, and every value is cut at 3000 characters. Two fields are special:

- `recaptcha`: a Google reCAPTCHA v3 token, required with a score of at least 0.5 when the app's secrets hold a reCAPTCHA private key; a token sent without a key also fails
- `xid`: an external ID from an earlier submission; if it names an existing ticket in the same organization, nothing is created and `ok` is returned

**Response.** `200` with:

```json
{"status": "ok", "xid": "<external id of the new ticket>"}
```

Most failures are also `200` with another `status`: `validation_error`, `recaptcha_error`, `not_found_error`, `duplicate_request_error` (identical payload within five minutes), `rate_limit_error` (the app's `rateLimit` setting), `internal_error` or `disabled_error`. Two cases return `400` with `{"error": "ValidationError", "details": "<message>"}` instead: an unknown connector ID and an `Origin` host outside the connector's `domain` list. When set, `logMessage` posts accepted submissions to a channel and `logJunk` posts rejected ones (reCAPTCHA or validation failure).

## POST /v1/email/\<id\>

Receives raw inbound email from the mail provider (Twilio SendGrid) for an [email connector](https://conclude.io/doc/reference/connectors/#email-connector). Your code never calls it; mail goes to the connector address from the [email guide](https://conclude.io/doc/apps/integrations/email/).

`<id>` is a connector ID or `any`, which resolves the connector from the `To`, `Reply-To`, `Cc`, `Bcc` and forwarding headers, then the envelope recipient. It returns `200` even for parse failures and mail dropped as definite spam, so the provider does not retry; only a missing ID (`404`) and the rate limit (`429`) differ. When no connector matches, the sender gets an automatic reply unless their message was one. No CORS headers are set.

## CORS

The form endpoints answer browsers from any origin: responses carry `Access-Control-Allow-Origin: *`, and an `OPTIONS` preflight on any `/v1` path returns `204` allowing `POST`, `GET`, `OPTIONS` and the `Accept`, `Accept-Encoding`, `Accept-Language`, `Content-Type` and `Content-Length` headers. Which sites may submit depends on the connector's `domain` setting, not on CORS: it matches the `Origin` host or a subdomain against a comma-separated list. Non-browser clients can send any `Origin`; use reCAPTCHA and `rateLimit` against abuse.

