Skip to main content
An AI Harness is your own agent runtime taking over a live Flowyte conversation. This guide is the one-time setup, start to finish: what you hand Flowyte, what you get back, how to prove the connection works, and how to run the loop afterwards. Follow it once and your harness is live. Two things move in opposite directions, and it helps to hold them apart from the start:

Flowyte → you

Signed webhooks to your endpoint_url. You verify them with the signing secret (whsec_…). This is the doorbell.

You → Flowyte

REST calls to the Flowyte API. You authenticate with a scoped connector key (flowyte_sk_…). This is where the truth lives.
Two different secrets, two different jobs. The signing secret only ever verifies inbound webhooks — it is not an API credential. The connector key only ever authenticates your outbound REST calls — it never appears in a webhook. Never swap them.

Before you start

  • The API base URL. Every path in this guide — /escalations, /escalations/{id}/messages — is relative to https://builder.flowyte.com/api/v1. So POST /escalations is POST https://builder.flowyte.com/api/v1/escalations. The MCP gateway, if you drive Flowyte that way instead, is a separate host: https://mcp.flowyte.com/mcp.
  • An AI Harness runtime you control, reachable at an absolute https:// URL. Flowyte SSRF-checks it: private, loopback and link-local targets are refused at registration and re-checked at send time.
  • A Flowyte organization with at least one agent.
  • For the sms channel: the organization’s A2P 10DLC (TCR) registration must be complete — the same registration its inbound number already needs. See SMS.
  • Starter or higher to run real conversations. Setup and the handshake test work on any plan, so you can wire everything up before upgrading.

1. Connect the harness integration

In the dashboard’s Integrations page, connect your AI Harness provider — hermes or openclaw (or POST /integrations/{kind}/connect). This is enablement only: it stores no credential, it just marks the provider connected for the organization. Nothing else on this page works until you do. Every escalation surface re-checks this server-side, so disconnecting the integration later immediately stops new conversations reaching your harness — it does not merely hide a button.

2. Give Flowyte your endpoint URL

This is the one thing you hand Flowyte. Register a destination — the record that says where conversations go and which channels you handle.
1

Create the destination

In Integrations → your harness → Add destination, give it a name, your endpoint_url, and tick the channels you handle (Chat, SMS, or both). Over the API this is POST /escalation-destinations with escalation_destinations:write.Optional knobs: context profile (which handoff bundle you receive), transcript window (how many recent turns come with it, default 20), claim window (how long Flowyte waits for you to claim before falling back — default 120 seconds), and a fallback route.
2

Copy the signing secret — it is shown once

The create response carries signingSecret (whsec_…) exactly once. It is never returned by any later read. Store it in your secret manager before you close the drawer; if you lose it, rotate it (POST /escalation-destinations/{id}/rotate-secret) rather than hunting for it.
The destination starts unverified. The handshake test in step 5 is the only path to active — no API call can set it directly.
Ticking SMS requires the organization’s 10DLC/TCR campaign to be active, or the destination refuses with 422 sms_registration_required. Chat has no such requirement.

3. Mint a connector key

In the dashboard’s Developer page, mint a secret key (flowyte_sk_…) for your harness. It is shown once. Every REST call carries it:
Grant only the scopes you need: The key is organization-scoped: it only ever sees its own tenant’s sessions, and a cross-tenant call fails closed as 404. These scopes are deliberately separate from escalation_destinations:* — a connector key can work conversations but cannot edit the destinations or agents it works for.

4. Verify webhook signatures

Flowyte signs every delivery. Verify before you do any work — parse nothing, queue nothing, and above all claim nothing on an unverified body.
A bad signature or a stale timestamp is a 401 from you, and no work.
Rotation, concretely. After rotate-secret, Flowyte sends Flowyte-Signature (new secret) and Flowyte-Signature-Prev (previous secret) for 24 hours. A connector still holding only the old secret keeps verifying, because its secret matches the -Prev header. Pick up the new secret inside that window; after it, only the new secret verifies.
Answer fast, then work. Flowyte counts any 2xx as delivered and gives you 10 seconds total. Acknowledge immediately and process asynchronously. A non-2xx or a timeout is retried up to 6 attempts (immediately, then 30s, 1m, 2m, 4m, 8m) and then dead-lettered. Delivery is at-least-once, so dedupe on Flowyte-Delivery.
Webhooks are the doorbell, not the mailbox. Never reconstruct a conversation from webhook bodies — read it from GET /escalations/{id}/messages, which is ordered and authoritative.

5. Pass the handshake test

The go-live gate. In Integrations → your harness → Handshake test, hit Run test (or POST /escalation-destinations/{id}/test). Flowyte opens a sandbox escalation on a synthetic conversation, delivers a signed escalation.test webhook, and then waits for your connector to complete the loop:
1

Verify the signature

Exactly as in step 4. Treat escalation.test as identical to escalation.requested — same code path, no special case.
2

Claim

POST /escalations/{id}/claim. Retry on 409 and 404: a redelivered webhook can transiently conflict, and the session may not be visible for a beat right after it is created. Claiming is idempotent for your own key.
3

Post one message

POST /escalations/{id}/messages with a client_message_id and text.
4

Resolve

POST /escalations/{id}/resolve.
The response is a step ledger — { webhook_delivered, claimed, message_posted, resolved } — plus a verdict and, on failure, a hint naming the first step you missed (for example “Connector claimed the session but posted no message within 120s. Check the escalations:respond scope on the connector key.”).
Both a pass and a fail come back as HTTP 200 — read result, not the status code. Only a green run flips the destination to active. A test message reaches no customer and is never billed; your connector does not need to know that, and should not branch on test.
Flowyte ships tools/mock-connector (Go, stdlib only) as a working reference for this whole loop, with a -fail-step flag to reproduce each failure hint.

6. Route real conversations to it

Set the agent’s escalation policy — PUT /agents/{id}/escalation-policy, or the Escalation policy page — as an ordered list of rules: when to escalate and where to route. Conditions are deterministic (an explicit request for a person, a human request, business hours, channel, a failed tool or knowledge lookup); routes are external:<destinationId>, an email fallback, or a queue. First match wins. The policy edits the agent draftpublish it so live traffic uses it. From then on, the loop is:
  1. escalation.requested arrives. Verify, dedupe, claim.
  2. Claim returns the context package: the recent transcript turns (each with its spine seq), the escalation reason, verified identifiers (on SMS, the customer’s phone), and completed or failed tool actions. It is rebuilt at claim time, so you start current.
  3. Reply with POST /escalations/{id}/messages. client_message_id is your idempotency key — a replay returns the original receipt instead of double-sending.
  4. Sync with GET /escalations/{id}/messages?after_seq=N and process strictly increasing seq.
  5. Finish with /resolve, /return (hand back to the Flowyte AI), or /request-human.
Claiming starts an activity-based lease — about 2 minutes on chat, 4 hours on SMS — and any owner action extends it, including a /messages read. Send /heartbeat if you go quiet but want to keep the thread. If the lease expires the thread returns to the AI and you get 409 lease_expired; re-claim to continue.

7. Start an outbound SMS thread (optional)

If your harness needs to open the conversation rather than wait for one, POST /escalations starts a new SMS thread and sends its first message. The session comes back already owned by your key — no claim, no SLA race — and the customer’s replies route to you exactly like any other escalation.
Four things decide whether this works, and they catch most first attempts:
  • Consent. The recipient must have a recorded basis. An inbound text is consent to text back — a customer who has ever texted the business is already conversational and needs nothing extra. A number that has never texted in is refused 422 consent_required.
  • It must be switched on. Outbound initiation is off by default and enabled per organization by Flowyte, on top of the deployment-level Agent Bridge switch. Otherwise 403 harness_outbound_disabled or 503 bridge_disabled.
  • There is no from. The sending number is derived from the agent’s SMS-active number, and the agent must have one (422 no_sms_number).
  • Send the message only. The brand identity and the Msg & data rates may apply. Reply HELP for help, STOP to opt out. block are appended for you on a first text. Writing your own ships it twice.
Quiet hours also apply differently here than to a reply: a first text is business-initiated, so outside the recipient’s legal window it is refused 409 quiet_hours with a nextOpen instant to retry after — never silently queued.
409 send_state_unknown is not a retry signal. It means the send outcome is genuinely unknown and the message may already have been delivered. Replay the same client_message_id (which resolves to the original answer and never sends a second text) or re-read the thread. A fresh id there texts somebody twice.
The full contract — every error code, the volume cap, the audited consent bypass, a worked end-to-end example — is in the connector reference.

When it does not work

Next

  • Connector reference — the full contract you keep open while building.
  • AI Harnesses — the operator-side view of the same setup.
  • MCP gateway — the same surface as agent tools, if your harness speaks MCP rather than REST.