HTTP
HTTP is a request-and-response protocol: a client sends a method, a path and headers, the server answers with a status code and usually a body, and the server keeps no memory of the exchange afterwards. Every Connect surface — the app, the public API, the MCP endpoint, inbound provider webhooks — is HTTP. Reading its status codes correctly is most of the work of debugging an integration.
The parts that matter#
GET /api/health HTTP/1.1
Host: connectbyjbrh.com
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/jsonThe method states intent, and the intent carries a promise. GET and HEAD must not change anything; PUT and DELETE must be safe to repeat with the same result; POST promises nothing at all, which is why every reliability mechanism in this reference exists mostly to make repeated POSTs survivable.
Statelessness is the property people forget. The server does not remember the previous request, so anything that must persist between two of them travels in a cookie, a token or a record. That is why a session is a separate topic, and why "it worked a second ago" is never on its own an explanation.
Codes that mean what they say#
| Code | What it means here | What to do |
|---|---|---|
| 200 | The request was handled. The body may still describe a refusal. | Read the body. A refusal is a decision, and Connect records it as one. |
| 401 | No usable session or key. | Authenticate. Do not retry the same request unchanged. |
| 403 | Authenticated, and this path is not permitted for this session. | Stop. On a customer session this is usually the customer_safe allowlist, not a bug in the feature. |
| 404 | No such path, or no such record *for you*. | Under row-level security, a record in another workspace is invisible rather than forbidden — 404 is the correct and deliberate answer. |
| 405 | The method is wrong for this path. | On the MCP endpoint this is expected: the 2026-07-28 revision is POST-only and answers GET or DELETE with 405. |
| 429 | Too many requests. | Back off. Slow the queue, do not widen it — see the rate-limiting page. |
| 5xx | The server failed. | Retry with backoff, but only if the request is safe to repeat. |
Codes that mislead#
- A 200 that is a failure
- An action can be refused, held for approval, or blocked by a suppression entry and still return 200, because the request itself succeeded. Treat the body as the result and the status as the transport.
- A 403 that is not about permission
- A customer session calling an Owner path is rewritten by
tenantAdapt, and anything it does not recognise becomes/api/customer/ui/blockedand a 403. The feature is not broken and the account is not restricted; the path simply has no customer form yet. - A 404 that means invisible
- Workspace isolation is enforced three times over — the allowlist, the ORM kernel, and PostgreSQL row-level security. A row you cannot see returns nothing, which is indistinguishable from it not existing. That is the intended behaviour, not a lookup failure.
Does Connect use HTTP?#
Used, everywhere. It is the only transport for the browser app, the public API, the MCP server, the A2A agent card, the machine-readable manifests and every inbound provider webhook. The one thing that is not plain request-and-response is the app's own event stream, which is server-sent events — still HTTP, but one long-lived response rather than many short ones.
/api/health is the health endpoint. There is no /healthz, no /health and no /readyz; a monitor pointed at one of those is checking nothing and will report green forever, or red forever, depending on how your prober treats a 404.
Questions#
Should a client treat every non-200 the same way?
No, and doing so is how integrations end up hammering a server that already told them to stop. 401 needs new credentials, 403 needs a different path or no path at all, 404 may be correct, 429 needs a slower queue and 5xx needs backoff. Only the last two are retryable without changing the request.
Why does a request that clearly did something return 200 with a refusal?
Because Connect distinguishes between the request failing and the action being declined. A refusal is recorded in the audit trail with the rule that produced it, which is more useful than a bare 4xx — and it means a client must read bodies rather than only counting statuses.
Is HTTP/2 or HTTP/3 different for an integrator?
Not semantically. Methods, statuses and headers mean the same things; the framing and multiplexing underneath change. Nothing in this documentation depends on the wire version.