# Champ email address
Source: https://docs.champ.ai/browser-profiles/champ-email-address
A per-profile email address — sign up for accounts with it, receive verification emails, and clear email 2FA
Every [managed profile](/browser-profiles/managed-profiles) can have its own **Champ email address**:
```
your-tenant-a1b2c3@mail.champ.ai
```
It works like a regular email address for the account the profile logs into. Emails sent to it show up on the
profile's **Email delivery** panel, and during logins Champ reads 2FA codes from it automatically.
## Generate the address
Open your profile under **Integrate → Browser Profiles** and click **Generate address** under **Champ email address**.
Selecting **Email** under Two-factor authentication also generates one automatically.
## What to use it for
* **Sign up with it** — when creating a fresh account for Champ to use, use the Champ address as the account's email.
Verification emails and 2FA codes land in Champ directly — nothing to forward or register. Works whether or not the
site uses 2FA.
* **Receive 2FA codes** — if the site emails one-time codes, get them to the Champ address and select **Email** under
Two-factor authentication. See [Email 2FA](/browser-profiles/email-2fa).
* **Receive account email** — password resets, magic links, and confirmations sent to the address are visible on the
profile page.
## Delivery history
The profile page shows an **Email delivery** panel listing each email's sender, subject, and whether a one-time code
was detected — never the email contents. Use it to confirm emails are actually arriving when a login or sign-up fails.
# Email 2FA
Source: https://docs.champ.ai/browser-profiles/email-2fa
Route email one-time codes to a Champ email address so managed logins clear 2FA
When a site sends its second-factor code by **email**, Champ reads the code from the profile's
[Champ email address](/browser-profiles/champ-email-address) and feeds it into the login automatically. Set this up once
per [managed profile](/browser-profiles/managed-profiles).
## Step 1 — Enable the Email method
Open your profile under **Integrate → Browser Profiles**, and under **Two-factor authentication** select **Email**. Champ
reads codes from the profile's [Champ email address](/browser-profiles/champ-email-address), generating one if the
profile doesn't have one yet.
Copy the address **before** your first login test, so the site has somewhere to send the code.
## Step 2 — Get codes to the Champ address
Pick whichever fits the site:
* **Create the account with the Champ address** — if you're setting up a fresh account for Champ to use, sign up with
the [Champ address](/browser-profiles/champ-email-address) as the account's email. 2FA codes (and any verification
emails) then land at the address directly — nothing to forward or register.
* **Register it directly** — if the site lets you set the email that receives 2FA codes, paste the Champ address there.
* **Forward from your inbox** — if codes go to a mailbox you control, forward them to the Champ address.
### Forward from Gmail
Following Google's [auto-forwarding guide](https://support.google.com/mail/answer/10957?hl=en):
1. In Gmail on the web, open **Settings (⚙️) → See all settings → Forwarding and POP/IMAP**, and click **Add a forwarding
address**.
2. Paste your Champ address and confirm. Google emails a confirmation to that address. Champ relays it back to
your own inbox as an **"Action required"** message — open it and click Google's confirmation link to authorize
forwarding.
3. Now scope a **filter** so only your 2FA mail is forwarded. Open **Settings → Filters and Blocked Addresses** and click
**Create a new filter**.
4. Match on your 2FA sender (**From**) or **Subject**, then click **Create filter**. Scoping the filter keeps unrelated
mail out and makes code detection more reliable.
5. Check **Forward it to** and pick your Champ address, then click **Create filter** to save.
## Step 3 — Test the login
Save with **Create & Test** (or run the login from the profile row). Champ logs in, waits for the code at the Champ
address, extracts it, and completes sign-in. It waits about two minutes for the code; if none arrives, the login is
cancelled — re-run it once forwarding is in place.
# Set up a managed profile
Source: https://docs.champ.ai/browser-profiles/managed-profiles
Store credentials once and let Champ log in (and re-authenticate) for you
A **browser profile** is a reusable, logged-in browser session that your workflows attach to when they act inside a
website. With **Managed Authentication** on, you store credentials once and the agent logs in for you, re-authenticating
automatically when the session expires. (Left off, the profile is manual — you log in live and Champ saves the session.)
For sites with two-factor authentication, finish with [Email 2FA](/browser-profiles/email-2fa) or
[SMS 2FA via Twilio](/browser-profiles/sms-2fa). Setting up a fresh account for Champ to use? Generate a
[Champ email address](/browser-profiles/champ-email-address) and sign up with it — verification emails and 2FA codes
then land in Champ directly.
## Create a managed profile
1. In the sidebar, open **Integrate → Browser Profiles** and click **Add Profile**.
2. Give it a **Name** and the **Login URL** (where the browser opens to log in).
3. Turn on **Managed Authentication** and provide credentials one of two ways:
* **1Password** — pick a vault and item. Champ reads the username, password (and TOTP, if present) at login time;
nothing is copied into Champ.
* **Manual** — type a **Username / Email** and **Password** directly.
Credentials are encrypted at rest and only readable by the tenant that created the profile. When editing, leave the
password blank to keep the current one.
## Choose a two-factor method
Under **Two-factor authentication**, pick how Champ obtains the second-factor code:
| Method | When to use |
| ------------- | ------------------------------------------------------------------------ |
| **None** | The site has no second factor. |
| **Email** | The site emails a code. → [Email 2FA](/browser-profiles/email-2fa) |
| **SMS** | The site texts a code. → [SMS 2FA via Twilio](/browser-profiles/sms-2fa) |
| **1Password** | The selected 1Password item carries a TOTP (authenticator) field. |
## Save and test
New managed profiles default to **Create & Test**: Champ saves a draft and immediately runs a real login to verify the
credentials and 2FA path. A profile that logs in successfully becomes **active**; use **Create without testing** to
verify later from the profile row. You can re-run the test anytime.
During login the agent enters the username and password (`x_user` / `x_pass`), waits for the 2FA code if one is set, then
submits it (`x_otp_code`). If the site doesn't challenge for a code this session, the agent treats that as success and
proceeds. Both prompts can be customized under **Customize prompts**, but the defaults work for most sites.
## When a session expires
When a managed session expires, Champ first tries to re-authenticate automatically using the stored credentials and 2FA
method — no action needed on your part. Only if that automatic re-login fails does Champ email the addresses under
**Advanced → Notification Emails**. The email links back to the profile, where you can re-authenticate in a live browser
and resume any waiting runs.
# SMS 2FA via Twilio
Source: https://docs.champ.ai/browser-profiles/sms-2fa
Read SMS one-time codes from your Twilio number so managed logins clear 2FA
When a site sends its second-factor code by **text message**, Champ reads the code from a phone number on your Twilio
account and feeds it into the login automatically. SMS 2FA is supported **only through Twilio**. Set this up once per
[managed profile](/browser-profiles/managed-profiles).
## Step 1 — Connect Twilio
Open **Integrate → Integrations**, connect the **Twilio** integration, and authorize it with your Twilio **Account SID**
and **Auth Token**. This is a one-time, tenant-wide step. The number that receives codes stays on your Twilio account —
Champ reads messages through your credentials; it doesn't provision or own the number.
## Step 2 — Point the site's 2FA at your Twilio number
On the target site, set the SMS verification number to a number on your Twilio account that can receive inbound SMS, so
codes land in your Twilio Messages log.
## Step 3 — Set the receiving number on the profile
In your profile under **Integrate → Browser Profiles**, under **Two-factor authentication** select **SMS**, then enter
your Twilio number in **Receiving phone number** using **E.164** format (a `+`, country code, then the number, no spaces):
```
+15551234567
```
## Step 4 — Test the login
Save with **Create & Test** (or run the login from the profile row). Champ logs in, watches your Twilio number for the
incoming code, extracts it, and completes sign-in. It waits about two minutes for the code; if none arrives, the login is
cancelled — re-run it once the number is wired up correctly.
# PHI & HIPAA Data Protection
Source: https://docs.champ.ai/enterprise/data-protection/phi-hipaa
How Champ protects Protected Health Information on data-protected workflow runs — encrypted, masked by default, decrypted only to run, and fully audited.
Champ automates healthcare workflows that may handle Protected Health Information (PHI). On a **data-protected workflow run**, all data fields are protected by default — Champ encrypts them, masks them, decrypts them only to run your workflows, limits who can reveal them, and logs every access. The safeguards below map to the HIPAA Security and Privacy Rules.
## Safeguards
**Encryption.** Sensitive fields are encrypted at rest (AES-256) in a dedicated vault with its own key, separate from your operational data, and encrypted in transit (TLS).
**Masked by default.** A sensitive value shows as `[redacted]` everywhere — UI, internal tooling, and AI. Seeing the real value always takes a deliberate, logged reveal.
**Decrypted only to run.** During automated processing, a value is decrypted only while your workflow runs — never stored in the clear afterward.
**Reveal is admin-only and always logged.** Only an admin in your organization can reveal a value, and a value can be revealed only when the access is recorded — there is no path to see a sensitive value without writing an audit entry. Champ staff cannot reveal values at all.
**Immutable audit log.** Every reveal is recorded — who, which field, when — in an append-only log that can't be edited or deleted. Logs hold no PHI and are kept at least six years (longer where state law requires), so purging PHI never erases the trail.
**Sensitive by default.** On a protected workflow every field is treated as sensitive unless you explicitly mark it otherwise, so encryption, masking, and logging apply automatically. Champ also rejects undeclared fields on protected workflows to prevent accidental leaks.
**Tenant isolation.** Each customer's data is isolated; no customer can reach another's.
**Unreadable outside production.** The key that decrypts sensitive data exists only in production — sensitive values cannot be read in any other environment.
**Retention & disposal.** Because sensitive values live in a separate vault, they can be purged on a schedule we agree — permanently erasing the values while your non-sensitive case history stays intact.
## How this maps to HIPAA
| Safeguard | How Champ addresses it |
| ------------------------------------------- | ------------------------------------------------------------------------------- |
| Access Control — §164.312(a)(1) | Reveal limited to your admins; tenant isolation |
| Audit Controls — §164.312(b) | Append-only log of every access to a sensitive value |
| Integrity — §164.312(c)(1) | Authenticated encryption detects tampering; audit log is insert-only |
| Encryption — §164.312(a)(2)(iv), (e)(2)(ii) | AES-256 at rest in an isolated vault; TLS in transit |
| Minimum Necessary — §164.502(b) | Masked by default; revealed one field at a time |
| Retention & Disposal — §164.310(d)(2)(i) | Vaulted values purgeable on an agreed schedule, independent of operational data |
## Working together
Champ provides these safeguards; you decide which data is sensitive and who has admin access.
We're happy to discuss a Business Associate Agreement (BAA) and review these controls with your security team. Questions? Contact your Champ representative.
# Champ AI VPC Deployment
Source: https://docs.champ.ai/enterprise/vpc-deployment/overview
Run workflow execution entirely inside your cloud account
Champ AI VPC Deployment runs workflow execution entirely inside your cloud account. Your PHI, prompts, screenshots, API responses, and workflow data never leave your VPC. Champ's control plane handles the workflow designer, orchestration, and run metadata — the parts that don't touch your data.
## Architecture at a glance
```mermaid theme={null}
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#fff5f0','primaryBorderColor':'#ff6b4a','primaryTextColor':'#1a202c','lineColor':'#4a5568','fontFamily':'DM Sans, sans-serif'}}}%%
flowchart LR
subgraph CP["☁️ Champ AI Control Plane • SaaS"]
direction TB
DS["Workflow
Designer"]
AU["User Auth
& SSO"]
CR["Case Records
(IDs, status, type)"]
OR["Run Orchestration
& Metadata"]
end
subgraph VPC["🔒 Your VPC • Data Plane"]
direction TB
W(["Champ Worker"])
PG[("PostgreSQL 15+
run state & artifacts")]
subgraph SB["Stateless Sandboxes"]
direction LR
BR["🌐 Browser"]
CE["⚙️ Code"]
LM["🤖 LLM Calls"]
end
CN["API Connectors"]
PRI[("Private APIs
EHRs, DBs, services")]
end
W <-.->|"outbound mTLS
metadata only"| CP
W --> PG
W --> SB
SB --> CN
CN --> PRI
classDef cp fill:#f7fafc,stroke:#cbd5e0,stroke-width:1px,color:#1a202c
classDef vpc fill:#fff5f0,stroke:#ff6b4a,stroke-width:1.5px,color:#1a202c
classDef sandbox fill:#ffffff,stroke:#e55a3d,stroke-width:1px,color:#1a202c
class CP cp
class VPC vpc
class SB sandbox
```
**Champ's control plane (SaaS)** handles the workflow designer, user auth, case records (IDs, status, metadata), run orchestration, and run metadata (IDs, statuses, timings, error classes).
**Your VPC (data plane)** handles workflow run execution, browser / code / LLM sandboxes, API connectors, and all run data and artifacts.
A lightweight worker deployed in your VPC opens an **outbound** mTLS connection to Champ's control plane, pulls work, executes it locally, and returns metadata only. Champ never initiates an inbound connection — no VPN, no PrivateLink, no firewall changes.
## How a run flows
```mermaid theme={null}
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#fff5f0','primaryBorderColor':'#ff6b4a','actorBorder':'#ff6b4a','actorBkg':'#fff5f0','noteBkgColor':'#f7fafc','noteBorderColor':'#cbd5e0','fontFamily':'DM Sans, sans-serif'}}}%%
sequenceDiagram
autonumber
participant U as User / Trigger
participant CP as Champ Control Plane
participant W as Worker (in VPC)
participant SB as Stateless Sandbox
participant D as Private Data / APIs
U->>CP: Start workflow run
CP->>CP: Create run metadata (IDs, statuses)
W-->>CP: Poll (outbound mTLS)
CP-->>W: Workflow definition + run ID
loop For each node
W->>SB: Spin up sandbox (browser / code / LLM)
SB->>D: Execute against private data
D-->>SB: Returns data
SB-->>W: Node result (stays in VPC)
W->>W: Persist to your Postgres
W-->>CP: Status + timing (metadata only)
end
Note over W,CP: No payloads, PHI, or artifacts cross the boundary
```
1. A user or trigger starts a workflow run from Champ's control plane.
2. The worker in your VPC polls over a single outbound mTLS channel.
3. The worker pulls the workflow definition and run ID.
4. For each node, the worker spins up a **stateless sandbox** — browser, code execution, or LLM call — inside your VPC.
5. Sandboxes reach private APIs, EHRs, and databases over your existing network.
6. Node results and artifacts are persisted to your managed Postgres.
7. Only run metadata (status, timings, error classes) flows back to the control plane.
## What stays in your VPC
* Protected health information (PHI) and sensitive business data
* Workflow run execution itself
* LLM prompts and responses
* Browser screenshots and DOM replays
* Tool outputs, API responses, connector results
* Secrets and credentials
* Run-time state: inputs, intermediate values, node results, artifacts
## What Champ holds on its side
* Workflow definitions you author
* **Case records** — case ID, status, case type, references to external records, workflow associations
* Run IDs and node IDs
* Per-step statuses, durations, error classes, retry counts
The metadata channel is schema-enforced on the wire — payload fields are structurally impossible to transmit.
## Stateless sandboxes
Every workflow node executes inside a sandbox that lives only for the duration of that node. Three types:
| Sandbox | Purpose |
| ------------------ | ----------------------------------------------------------------------------------------------------------- |
| **Browser** | Headless browser sessions for web workflows — form fills, navigation, DOM interaction, screenshots. |
| **Code execution** | Short-lived runtime (Python / JS) for data transformation and custom logic. |
| **LLM calls** | Outbound calls to your chosen LLM provider using your credentials. Prompts and responses never touch Champ. |
Sandboxes are stateless and ephemeral: no state persists between runs, and they are torn down as soon as the node completes. All durable state lives in your managed Postgres.
## Deployment options
| Option | Best for |
| -------------------------------------------- | -------------------------------------------------------------------- |
| **Helm chart on Kubernetes** *(recommended)* | Production deployments on EKS, GKE, AKS, or on-prem Kubernetes |
| **Docker Compose** | Proof-of-concept, single-engineer trials, non-production evaluations |
The Helm chart provides high availability (multi-replica, multi-AZ), horizontal autoscaling (worker pods scale on queue depth), clean upgrades and rollbacks, and integrates with standard Kubernetes security and observability tooling.
## Infrastructure you provide
Champ AI runs on infrastructure you already operate under your existing compliance posture. The required footprint is intentionally minimal:
| Component | Service |
| ------------------ | ----------------------------------------- |
| Kubernetes cluster | EKS / GKE / AKS / on-prem |
| PostgreSQL 15+ | RDS / Cloud SQL / Aurora / Azure Database |
Postgres is the only stateful dependency — it holds run state, the job queue, and run artifacts (screenshots, browser replays). No Redis, no object store, no external secret manager is required. Minimum sizing, network policies, IAM roles, and required Postgres extensions are documented in the deployment guide.
## Your choices
**LLM provider (BYO key).** You bring your own LLM credentials from one of **Anthropic, OpenAI, or Google**. Champ's workers call the provider directly from inside your VPC using your keys. Prompts and responses never transit Champ's infrastructure.
**Connectors and APIs.** External providers (EHRs, internal services, databases) are reached over your existing private network. Champ never routes this traffic.
**Artifact handling.** You define retention, encryption, and access policies for screenshots, browser replays, and tool outputs.
**VPC management.** Optionally, Champ can operate the VPC deployment on your behalf under scoped access controls, minimizing your engineering team's involvement.
## Security model
* mTLS between the in-VPC worker and Champ's control plane
* Outbound-only traffic — no inbound connections from Champ
* All customer data encrypted at rest in your managed Postgres
* Workflow sandboxes are ephemeral and private-subnet
* Secrets remain in your existing secret manager (AWS Secrets Manager, Vault, etc.)
## Compliance posture
Because all data processing occurs inside your VPC using your existing compliance-reviewed infrastructure (managed Postgres, KMS, IAM), Champ AI VPC Deployment **inherits your HIPAA / SOC 2 / HITRUST controls** for data handling. Champ's BAA covers the control-plane metadata only.
## Why choose VPC deployment
* **Data never leaves your boundary.** PHI, prompts, screenshots, and API responses stay under your encryption and access controls.
* **No inbound connectivity required.** Outbound mTLS only — no VPN, PrivateLink, or firewall changes to onboard.
* **Inherit your compliance posture.** Runs on your audited, BAA-covered infrastructure.
* **BYO LLM keys.** Your Anthropic / OpenAI / Google account, your rate limits, your audit trail.
* **Single stateful dependency.** Postgres only — no Redis, no object store, no external secret manager.
# Connect 1Password
Source: https://docs.champ.ai/integrations/1password
Link a 1Password service account so managed browser profiles can read credentials at login time
Connecting **1Password** lets your [managed browser profiles](/browser-profiles/managed-profiles) pull a username,
password, and TOTP (authenticator) code straight from your vault at login time. Nothing is copied into Champ — the agent
reads the item only when it logs in. The connection is tenant-wide: set it up once and it's available to every profile.
## Step 1 — Create a 1Password service account
Champ authenticates to 1Password with a **service account token**, not your personal login.
1. Follow 1Password's
[Create a service account guide](https://www.1password.dev/service-accounts/get-started#create-a-service-account).
2. Grant the service account **read** access to the vault(s) that hold the credentials your profiles will use. A
service account can only see vaults you explicitly share with it.
3. Copy the token (it starts with `ops_`) when 1Password shows it. The token is displayed **once** — store it somewhere
safe before leaving the page.
Scope the service account to only the vaults Champ needs. You can revoke or rotate the token anytime from the 1Password
developer console, which immediately cuts off access.
## Step 2 — Connect it in Champ
1. Navigate to [**Integrations**](https://dash.champ.ai/app/integrations) in the sidebar.
2. Under **Credential Vaults**, find the **1Password** card and click **Connect**.
3. Paste your service account token (`ops_…`) into the dialog and click **Connect**.
Once connected, the card shows a green **Connected** badge. If the token is rejected, double-check that you copied the
full value and that the service account has access to at least one vault.
## Step 3 — Use it on a browser profile
With 1Password connected, the **Managed Authentication** section of any
[browser profile](/browser-profiles/managed-profiles) lets you source credentials from your vault instead of typing them:
1. Open **Integrate → Browser Profiles** and add or edit a profile.
2. Turn on **Managed Authentication** and choose **1Password**.
3. Pick the **vault** and **item**. Champ reads the username and password at login.
4. If the item carries a TOTP field, you can select **1Password** under **Two-factor authentication** to clear
authenticator-based 2FA automatically.
## Disconnecting
Click **Disconnect** on the 1Password card to remove the token. Browser profiles that rely on 1Password credentials will
stop logging in until you reconnect, so re-point or update those profiles first.
# Download case files
Source: https://docs.champ.ai/integrations/case-files
Fetch files a workflow attached to a case from your own backend
Workflows can attach files to a case — a document downloaded by a browser step, a file pulled from a URL or
Google Drive. When the workflow **exports** such a file, your backend can download it through a stable URL using
the same API token as the [cases API](/integrations/cases-api).
The flow is:
1. In the workflow, a **File** step attaches the file to the case with **Export file** enabled.
2. The workflow writes the file's URL into a case attribute (a **Case update** step referencing the File step's
`url` output).
3. Your backend reads the attribute from the [cases API](/integrations/cases-api#fetch-a-case) and fetches the
URL with your API token.
## The file URL
An exported case file is addressed by a stable URL:
```
https://download.champ.ai/f/{FILE_ID}
```
The URL never expires and never changes — it's safe to store in your own database and fetch years later. It also
grants nothing by itself: anyone holding the URL without a valid API token gets a `401`, and even a valid token
from a different organization gets a `404`, so passing it around in tickets, emails, or logs doesn't expose the
file.
## Download a file
Send a `GET` request with your API token:
```bash theme={null}
curl -L https://download.champ.ai/f/{FILE_ID} \
-H "Authorization: Bearer "
```
Path parameter. The file's ID — the last segment of the URL the workflow wrote into the case attribute. You
never construct this yourself; always use the full URL the workflow produced.
Request header. `Bearer ` — an API-scoped token from the
[Tokens page](https://dash.champ.ai/app/tokens), the same token the cases API uses.
The response is a `302` redirect: `Location` holds a short-lived storage link (valid for about a minute) that
serves the bytes, and the body carries the same link as JSON for clients that prefer an explicit second request:
```json theme={null}
{ "url": "https:///…?signature=…" }
```
Most HTTP clients follow the redirect automatically and just receive the file — modern curl, `fetch`,
python-requests, and Go all drop the `Authorization` header on the cross-host hop, which is exactly right.
If your client forwards the `Authorization` header to the redirect target, the storage host rejects the request
with a `400` — it refuses requests carrying two credentials. If you hit that, disable automatic redirects and
make two explicit requests: `GET` the file URL with your token, then `GET` the `url` from the JSON body (or the
`Location` header) with **no** `Authorization` header.
The final response serves the file with its original filename in `Content-Disposition`.
## Exporting a file
Only **exported** files can be downloaded externally. Export is a per-file decision the workflow makes: the
**External access** setting on the File step. A file attached as **Internal only** stays internal — its URL still
works inside the workflow (for example as an LLM file input), but external requests get a `404`.
Once a run exports a file it stays exported; retries of the step don't change that.
There is no listing endpoint — your systems learn about a file when the workflow hands its URL over, normally
through a case attribute you read from the cases API or receive in a
[webhook payload](/webhooks/payloads).
## Pre-signed links
If your systems can't call an authenticated API at all — the file needs to reach a human clicking a link in a
ticket or email — the File step offers a third mode: **Export + pre-signed link**. Besides exporting the file, the
step's output then includes a second URL, `presigned_url`, that serves the file directly with **no API token**.
Trade-offs versus the stable URL:
* **It expires after 7 days.** Don't store it — anyone clicking later gets an error. The stable URL on the same
output never expires.
* **It grants access by itself.** Anyone holding the link can download the file until it expires, so treat it
like the file's contents, not like an identifier.
* **It can't be revoked** — the only control is the 7-day expiry.
Use the stable URL whenever your backend can send an API token; reach for the pre-signed link only when it can't.
## Errors
| Status | When |
| ------ | --------------------------------------------------------------------------------------------------------------------------------- |
| `401` | Missing or invalid `Authorization` header. |
| `404` | The file doesn't exist, belongs to another organization, or hasn't been exported. These cases are deliberately indistinguishable. |
Errors return a JSON body with an `error` message.
# Manage cases via REST API
Source: https://docs.champ.ai/integrations/cases-api
Open, read, and update cases for a direct case workflow programmatically
A **direct case workflow** can have cases created from outside Champ with a single HTTP request — from your own backend, a form handler, or another tool. Each case you create kicks off the workflow's automation against the data you submit.
The flow is:
1. Create an **API token** in the dashboard.
2. `POST` a case to your workflow's cases endpoint with its `attributes`.
3. Fetch the case to read its current status and attributes.
4. Optionally, update the case's status — for example to close it from your own system.
This endpoint only works for workflows whose case source is **direct**. Ticketing and email workflows take in cases through their own channels and can't be written to with this API.
## Create an API token
1. Go to [**Tokens**](https://dash.champ.ai/app/tokens) in the dashboard.
2. Click **Create Token**, choose the **API** scope, and click **Create**.
3. Copy the token immediately — it's shown **once** and cannot be retrieved again. API tokens look like `api_xxxxxxxx…`.
The token is scoped to your organization. Treat it as a secret: store it in an environment variable or secrets manager, and delete it from the Tokens page if it's ever exposed.
## Find your workflow ID
Open the direct case workflow in the dashboard. The numeric ID is the last segment of its URL (`https://dash.champ.ai/app/workflows/`). You'll use it in the request path below.
## Create a case
Send a `POST` request to the workflow's cases endpoint:
```bash theme={null}
curl -X POST https://api.champ.ai/api/external/v1/workflows/{WORKFLOW_ID}/cases \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{"subject": "Refund request", "attributes": {"amount": 42, "reason": "late delivery"}}'
```
Path parameter. The numeric ID of a **direct case** workflow.
Request header. `Bearer ` — an API-scoped token from the Tokens page.
Request body. A human-readable title for the case. If you omit it or leave it blank, Champ stamps a timestamped
default like `Refund Intake via API - 2026-05-28 17:04:12 UTC`.
Request body. The case's field values, validated against the workflow's attribute schema. Any attribute that is **required** in the workflow must be supplied here. Omit it (or pass `{}`) for workflows with no required attributes.
Request body. `"live"` (default) or `"test"`. Test cases run the workflow end to end but don't count as real work — they're excluded from webhooks, billing, metrics, and digests, and their runs execute in test mode. Point your staging environment here.
The body accepts only `subject`, `attributes`, and `mode`. Any other top-level key is rejected with a `422` before the case is created.
The case is created immediately and the workflow's automation starts asynchronously. The response is the new case:
```json theme={null}
{
"case": {
"id": 987,
"workflow_id": 123,
"subject": "Refund request",
"status": "Open",
"outcome": null,
"mode": "live",
"attributes": { "amount": 42, "reason": "late delivery" },
"created_at": "2026-05-28T17:04:12Z",
"updated_at": "2026-05-28T17:04:12Z"
}
}
```
| Field | Description |
| ------------- | --------------------------------------------------------------------------- |
| `id` | Unique case ID. Use it to fetch the case. |
| `workflow_id` | The workflow this case belongs to. |
| `subject` | The case title. |
| `status` | Case status. New cases start as `Open`; the workflow updates it as it runs. |
| `outcome` | Disposition of a `Closed` case (e.g. `Resolved`); `null` otherwise. |
| `mode` | `"live"` or `"test"`. See [Test cases](#test-cases). |
| `attributes` | The case's current field values. |
## Fetch a case
Read a case's current status and attributes by ID:
```bash theme={null}
curl https://api.champ.ai/api/external/v1/workflows/{WORKFLOW_ID}/cases/{CASE_ID} \
-H "Authorization: Bearer "
```
The response is the same `case` shape as above. As the workflow runs, the `status` and `attributes` reflect the latest state.
Cases normally evolve through the workflow's own automation. To watch a case progress, poll this endpoint or [set up a webhook subscription](/webhooks/subscriptions).
## Update a case's status
Move a case to a new status yourself — for example to close it when it's resolved on your side, or to reopen it:
This operation works for cases from every supported workflow source, including direct, ticketing, email, and Slack.
Creating and fetching cases through this API remains limited to direct case workflows.
```bash theme={null}
curl -X PATCH https://api.champ.ai/api/external/v1/workflows/{WORKFLOW_ID}/cases/{CASE_ID} \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{"status": "Closed"}'
```
Request body. The status to move the case to. Must be one of the statuses declared on the workflow (see [Case statuses](#case-statuses)). `Error` is engine-managed and can't be set through the API.
This is more than writing a field: the update is recorded on the case's timeline and **starts the workflow run
attached to the new status**, which counts toward your usage like any other run. Use it when you intend the case to
actually move, not to sync statuses.
The response is the updated `case` in the same shape as above.
The body accepts only `status`. Any other top-level key is rejected with a `422`.
## Test cases
Create a case with `"mode": "test"` to exercise a workflow without creating real work:
```bash theme={null}
curl -X POST https://api.champ.ai/api/external/v1/workflows/{WORKFLOW_ID}/cases \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{"subject": "Staging smoke test", "attributes": {"amount": 1}, "mode": "test"}'
```
Test cases behave like live ones — the workflow runs end to end, statuses transition, attributes update — but they are excluded from webhooks, billing, metrics, and reporting digests, and their runs are marked test mode. This makes them safe to fire from a staging environment or CI check.
Test cases still execute the workflow's real steps. If your workflow calls external systems or sends messages, design it to branch on the case's `mode` where those actions shouldn't run for test traffic.
## Case statuses
Every case carries a `status`. A new case always starts as **`Open`**, and the workflow's automation moves it
through the rest as it runs. You can also move it yourself with the [update endpoint](#update-a-cases-status).
Each workflow ships with three **default statuses** that are always present:
| Status | Meaning |
| -------- | ------------------------------------------------------------------------ |
| `Open` | The case is live and the workflow is working it. Every case starts here. |
| `Closed` | The case is resolved and done. The workflow has finished with it. |
| `Error` | The workflow hit a problem it couldn't resolve and parked the case. |
A workflow can also define **custom statuses** on top of these defaults (for example `Awaiting customer` or
`Pending review`) to model the intermediate stages of its own process. A case's `status` is always one of the
statuses declared on its workflow, so the exact set you'll see depends on how that workflow is configured — open the
workflow in the dashboard to view its full status list.
A case can only ever be in a status its workflow has defined. `Error` is set by the engine when a run fails and
can't be set through the API — recover an errored case by moving it to another status.
## Validation errors
If `attributes` doesn't match the workflow's schema — a missing required field, a wrong type — the request returns `422` with a `validation_errors` array. Each entry points to the offending field with a [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901):
```json theme={null}
{
"error": "attributes_validation_failed",
"validation_errors": [
{ "path": "/amount", "error": "value at `/amount` is not a number" }
]
}
```
## Errors
| Status | When |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401` | Missing or invalid `Authorization` header. |
| `404` | Workflow or case not found, or it belongs to another organization. |
| `422` | The workflow isn't a direct case workflow, an unsupported body key was sent, `attributes` wasn't an object, `attributes` failed validation, or a status update sent a status the workflow doesn't define. |
Errors return a JSON body with an `error` message.
# Build workflows with MCP
Source: https://docs.champ.ai/integrations/mcp
Connect Claude, ChatGPT, Codex, or any MCP client to Champ and let an AI agent build, edit, and test your workflows
Champ exposes a [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server, so AI agents — Claude,
ChatGPT, Codex, Claude Code, Cursor, or any MCP-compatible client — can work in your Champ workspace directly.
A connected agent can list and read your workflows, create and edit workflow drafts node by node, run and test
them, and inspect run results. Publishing a draft always remains a human action in the dashboard.
## Connect (recommended)
Open the [Tokens page](https://dash.champ.ai/app/tokens) and copy the Connect URL shown for the workspace you are
viewing. It is already configured for that workspace, so there is no access token to create.
Your client opens a browser sign-in the first time; approve access and you're connected. Access follows your
workspace membership: it's tied to your login, refreshes silently, and ends automatically when your account is
deactivated.
To connect another workspace, switch workspaces in Champ and copy that workspace's Connect URL.
In Claude on the web or desktop:
1. Open **Settings → Connectors** and choose **Add custom connector**.
2. Paste the Connect URL and add it.
3. Click **Connect**, sign in with your Champ account, and approve access.
On Claude Team/Enterprise plans, an admin may need to allow custom connectors first.
ChatGPT creates custom MCP connections as plugins. Initial setup happens on the
[ChatGPT website](https://chatgpt.com), even if you plan to use Champ from the desktop app. The desktop app
does not expose the website's **Security and login** settings.
1. On chatgpt.com, open **Settings → Security and login** and enable **Developer mode**.
2. Open [ChatGPT Plugins](https://chatgpt.com/plugins), select **+**, and add a name, description, and the
Connect URL.
3. Create and install the plugin, sign in with your Champ account, and approve access.
4. Start a new Chat or Work conversation and select Champ from the tools menu.
The installed plugin is also available from the merged ChatGPT desktop app's **Plugins** directory. Developer
mode availability depends on your account and workspace policy. See OpenAI's
[MCP plugin setup](https://developers.openai.com/plugins/deploy/connect-chatgpt) for the current product flow.
Codex is now a dedicated view inside the
[ChatGPT desktop app](https://learn.chatgpt.com/docs/app). It connects directly to MCP servers and shares that
local configuration with Codex CLI and the IDE extension on the same computer.
In the desktop app:
1. Open the app menu or press `Cmd+,` on macOS or `Ctrl+,` on Windows, then choose **MCP servers**.
2. Select **Add server**, choose **Streamable HTTP**, paste the Connect URL, save, and then select **Restart**.
3. Select **Authenticate**, sign in with your Champ account, and approve access.
Or configure and authenticate from Codex CLI:
```bash theme={null}
codex mcp add champ --url ""
codex mcp login champ
```
This direct MCP configuration is local to the Codex host; it is different from the account-level ChatGPT
plugin above. See OpenAI's [Codex MCP guide](https://learn.chatgpt.com/docs/extend/mcp).
```bash theme={null}
claude mcp add champ --transport http ""
```
Then run `/mcp` inside Claude Code and choose **Authenticate** — a one-time browser sign-in; later sessions
refresh silently.
### Other clients
Most MCP clients that support OAuth (Cursor, VS Code, Windsurf) work the same way: add the URL, then trigger
the client's connect/login action. Cline is not supported — its MCP implementation lags the current spec.
## For CLI, CI & scripts: tokens
Headless environments can't run a browser sign-in — use an MCP token instead:
1. Go to [**Tokens**](https://dash.champ.ai/app/tokens) in the dashboard (avatar menu → **API/MCP Tokens**).
2. Click **Create Token**, choose the **MCP** scope, and click **Create**.
3. Copy the token immediately — it's shown **once** and cannot be retrieved again. MCP tokens look like
`mcp_xxxxxxxx…`.
Send it as an `Authorization: Bearer` header:
```json theme={null}
{
"mcpServers": {
"champ-workflows": {
"type": "http",
"url": "https://api.champ.ai/api/external/v1/workflows_mcp",
"headers": {
"Authorization": "Bearer "
}
}
}
}
```
Tokens don't expire; to revoke one, delete it from the Tokens page and any client using it loses access
immediately. The MCP and API scopes are separate — an `api_…` token is rejected by the MCP endpoint and vice
versa.
## What a connected agent can do
Your client discovers the full tool list automatically (in Claude Code, run `/mcp`). The core groups:
| Group | Tools |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Read workflows | `list_workflows`, `get_workflow`, `get_workflow_node`, `list_workflow_tools`, `list_workflow_triggers` |
| Build & edit drafts | `create_workflow`, `create_workflow_draft`, `update_workflow`, `update_workflow_node`, `add_workflow_nodes_and_edges`, `insert_workflow_node`, `remove_workflow_node`, `move_workflow_node` |
| Run & test | `workflow_runs`, `test_workflow_node`, `node_test_cases` |
| Cases | `cases`, `case_stats`, `case_reviews` |
The server also exposes tools for browser profiles, hybrid-browser step building, document and template
handling for Fill nodes, and organization artifacts and memory — the tool descriptions your client fetches are
the authoritative reference.
## How editing works
MCP editing follows the same draft → publish model as the dashboard:
1. **Edits apply to drafts only.** To change a published workflow, the agent first calls
`create_workflow_draft`, which opens (or reuses) that workflow's draft revision. Brand-new workflows created
with `create_workflow` start as drafts.
2. **The graph is edited incrementally** — one node or edge at a time — so the agent can read back and verify
each change.
3. **Test before publishing.** `test_workflow_node` runs a single node in isolation without saving anything;
`workflow_runs` can execute the full draft as a test run; `node_test_cases` saves repeatable test cases with
assertions.
4. **Publishing is human-only.** No MCP tool can publish a draft. The agent can pre-fill the commit message
(`suggested_commit_message` on `update_workflow`), but a person reviews and publishes from the dashboard.
## Security notes
* Both sign-in and tokens grant the full tool set, scoped to your workspace — there are no per-tool permissions.
* Sign-in access is tied to the person: it follows workspace membership and ends when the account is
deactivated. Actions are attributed to the signed-in user.
* Tokens are the opposite trade: they never expire, are visible to and deletable by anyone in your organization
on the Tokens page, and attribute actions to the user who created them. Treat a token like a password and
store it in a secrets manager.
# Add a REST API integration
Source: https://docs.champ.ai/integrations/rest-api
Connect a REST endpoint as a tool your workflows can call
REST API integrations let your workflows call any HTTP endpoint from inside an agent node's toolset. Once you add a connection, it appears under **Tool Integrations** on the Integrations page and can be used to build REST queries that agents invoke as tools.
## Create a REST API connection
1. Navigate to [**Integrations**](https://dash.champ.ai/app/integrations) in the sidebar.
2. Under **Tool Integrations**, find the **REST API** card and click **Connect**.
3. Fill in the fields below and click **Create**.
### Fields
A human-readable label for the connection (e.g. "Acme CRM"). Shown in the tools list when building workflows.
The root URL for the API (e.g. `https://api.acme.com/v1`). REST queries built against this connection will append their path to this base.
Optional link to the provider's API documentation. Surfaced in the UI when you're building queries against this connection.
How Champ should authenticate each request. See [authentication types](#authentication-types) below.
Optional key/value pairs sent on every request (e.g. `X-API-Version: 2024-01`). Click **+ Add Header** to add a row.
Optional safeguard to cap outbound traffic. Entered inline as **N requests per M minute(s)** (default `100` / `1`). Champ will queue or error additional requests once the limit is hit.
### Authentication types
| Type | Fields | Behavior |
| ---------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| **None** | — | No auth header added. Use for public APIs. |
| **Bearer** | Bearer Token | Sends `Authorization: Bearer ` on every request. |
| **Basic** | Username, Password | Sends HTTP Basic authentication. |
| **Custom** | Auth Payload (JSON) | Advanced: pass a JSON object handled by the integration engine. Use this for custom header schemes or providers that don't fit the patterns above. |
Credentials are encrypted at rest. Only the tenant that created the connection can read them.
## Use the connection in a workflow
After saving, the connection is available for building REST queries:
1. On the Integrations page, open **New REST Query**.
2. Select your connection under **Connection**.
3. Define the path, HTTP method, parameters, and response shape.
4. The query is now available as a tool in any workflow agent node's tool list — the agent will call it when its instructions require the data.
## Editing and removing
* Click any connection in the **Tool Integrations** table to edit its fields. Changes apply to the next request; in-flight queries use the previous values.
* Deleting a connection breaks any REST queries built against it. Remove the queries first, or they'll fail at runtime.
# Connect a ticketing platform
Source: https://docs.champ.ai/integrations/ticketing
Authorize Zendesk, Intercom, Jira and other ticketing systems so workflows can read and act on your tickets
Connecting a ticketing platform authorizes Champ to call that system's API on your behalf. Once connected you can:
* **Call the platform from a workflow** — an API node picks the connection and Champ attaches the credentials for you.
* **Sync tickets into cases** — a scheduled poll turns tickets into [cases](/integrations/cases-api) that run a case
workflow.
* **See usage** — every call a workflow makes through the connection is logged on the connection's detail page.
Champ manages the OAuth handshake and refresh, so you never paste an API key or store a token yourself.
## Supported platforms
| Platform | Ticket automation | Cases sync |
| -------------- | :---------------: | :--------: |
| **Zendesk** | ✓ | ✓ |
| **Intercom** | ✓ | ✓ |
| **Jira** | ✓ | ✓ |
| **Salesforce** | ✓ | ✓ |
| **HubSpot** | ✓ | ✓ |
| **Kustomer** | ✓ | ✓ |
| **ServiceNow** | ✓ | — |
Need something that isn't listed? Any HTTP API can be wired up as a [REST API integration](/integrations/rest-api).
## Step 1 — Connect the platform
1. Navigate to [**Integrate → Integrations**](https://dash.champ.ai/app/integrations) in the sidebar.
2. Find your platform under **Ticketing Platforms** and click **Connect**.
3. A secure connect window opens. Sign in to the platform and approve the permissions it asks for.
Some platforms ask for one extra detail in that window so Champ knows which account to talk to — your Zendesk
**subdomain**, your ServiceNow **instance**, or which Jira **site** to authorize. Sign in as a user whose permissions
cover everything you want automated: the connection can only do what that account can do.
When the connection succeeds, the platform moves out of the available list into a **Connected** table showing its base
URL and the date it was connected.
Connect each platform once per workspace. Everyone on your team shares the connection — there's no need for each
person to authorize their own.
## Step 2 — Use it in a workflow
Add an **API** node to any workflow and select your platform under **Integration Connection**. Champ attaches the
credentials to every request, so you only describe the call:
1. Choose the **HTTP Method**.
2. Enter the **Path** — the connection's base URL is already prefilled, so a Zendesk call is just
`/api/v2/tickets.json`, and a Jira call is just `/rest/api/3/issue/SUP-1`.
3. Fill in headers and a body if the endpoint needs them.
The node's **View API Docs** link opens the platform's API reference so you can look up the right endpoint without
leaving the builder.
A few platforms (Kustomer, for example) route through Champ without a stored base URL. For those the node asks for a
full **URL** instead of a path — paste the whole endpoint.
## Step 3 — Sync tickets into cases
A **sync** polls the platform on a schedule and turns each new or updated ticket into a case, which then runs your case
workflow. You need a case workflow with a **ticketing** case source before you start — create one under **Workflows**
first.
1. Go to [**Integrate → Cases Sync**](https://dash.champ.ai/app/cases-sync) and open the **Data Syncs** tab.
2. Click **+ Add Sync** and fill in the form:
Which connected platform to pull from. Only platforms that support cases sync appear here.
The record to sync — Zendesk `Ticket`, Intercom `Conversation`, Jira `Issue`, Salesforce `Case`, and so on. Most
platforms offer exactly one, in which case it's fixed for you.
A name for this sync, shown in the syncs table. Defaults to the object type.
How often to poll. Defaults to 5 minutes. (Zendesk is locked to 5 — it syncs on Champ's own schedule.)
The ticketing case workflow each synced ticket runs. If the dropdown is empty, you don't have one yet.
Advanced. How far back to look on the first run, before the sync has a cursor. Defaults to 7 days on most platforms.
Advanced. How many records to pull per request. Leave the default unless you're tuning a large backfill.
3. Save, then **turn on the Enabled switch** in the syncs table. New syncs are created switched off so you can review
them before any data moves.
A sync is off until you flip the **Enabled** switch. If **Last Sync** stays "Never", that's almost always why.
The syncs table tracks each config's interval, last sync time, and how many records it pulled in the last 24 hours.
Click any row to edit it, or the trash icon to delete it. Deleting a sync stops future polling — cases it already
created stay put.
Each poll only writes when something actually changed, so a ticket that's been sitting untouched won't re-trigger its
case workflow.
## Reviewing and reconnecting
Click a connected platform on the Integrations page to open its detail page, which shows the workflow runs it triggered,
every API call workflows made through it, and which workflows are using it.
To re-authorize — after rotating credentials, changing the account, or if calls start failing with authorization errors
— click **Re-connect** on the platform and run through the connect window again. This replaces the existing
authorization; workflows and syncs pointing at the connection keep working and don't need to be rebuilt.
# Welcome to Champ AI
Source: https://docs.champ.ai/introduction
Developer documentation for building with Champ AI workflows
Champ AI automates multi-step business processes using browser agents, LLM tools, and integrations. Three ideas underpin everything: a **case** is one piece of work — an enrollment, a claim, a support request; a **workflow** defines how cases of that type get handled; a **run** is one execution of a workflow against a case. Cases are the durable record and the entry point — create a case (via API, sync, email, or by hand) and its workflow takes it from there.
These docs cover the pieces you'll typically wire up yourself: connecting external APIs as tools your workflows can call, creating cases over HTTP, and receiving webhooks when cases change or runs complete.
## Guides
Authorize Zendesk, Intercom, Jira and more, then sync tickets into cases.
Connect any HTTP endpoint as a tool your workflows can call.
Create cases programmatically to kick off workflow automation.
Connect Claude Code or any MCP client and let an agent build, edit, and test workflows.
Store credentials once and let the agent log in — including email and SMS 2FA.
Get notified when workflow runs complete or fail.
Payload shape, retry behavior, and handling guidance.
# Webhook payloads & delivery
Source: https://docs.champ.ai/webhooks/payloads
Payload shape, retry behavior, and handling guidance
## Payload shape
Every webhook is an HTTP `POST` with `Content-Type: application/json`. The body shape depends on the event family — workflow run events carry `data.workflow_run`; case events carry `data.case`.
### Workflow run events
`workflow_run.completed` and `workflow_run.failed` carry the run that fired them:
```json theme={null}
{
"event": "workflow_run.completed",
"timestamp": "2026-04-22T10:30:45Z",
"data": {
"workflow_run": {
"id": 12345,
"workflow_id": 67890,
"status": "completed",
"input": { "...": "the input the run was started with" },
"output": { "...": "the workflow's final output" },
"created_at": "2026-04-22T10:25:00Z",
"updated_at": "2026-04-22T10:30:45Z"
}
}
}
```
| Field | Description |
| ------------------------------- | --------------------------------------------------------------------------------------------- |
| `event` | One of `workflow_run.completed` or `workflow_run.failed`. |
| `timestamp` | When the event was dispatched, in ISO 8601 UTC. |
| `data.workflow_run.id` | Unique ID of the workflow run. Use this for deduplication. |
| `data.workflow_run.workflow_id` | The workflow that produced this run. Use to filter if you only care about specific workflows. |
| `data.workflow_run.status` | `completed` or `failed`. Mirrors `event` but is convenient to switch on. |
| `data.workflow_run.input` | The input payload the run was started with. |
| `data.workflow_run.output` | The workflow's final output. For failed runs this may include error details. |
| `data.workflow_run.created_at` | When the run started (ISO 8601 UTC). |
| `data.workflow_run.updated_at` | When the run reached its terminal state (ISO 8601 UTC). |
### Case events
`case.closed` and `case.error` carry the [case](/integrations/cases-api) whose status changed:
```json theme={null}
{
"event": "case.closed",
"timestamp": "2026-04-22T10:30:45Z",
"data": {
"case": {
"id": 12345,
"subject": "Acme Corp — renewal",
"status": "closed",
"workflow_id": 67890,
"attributes": { "...": "the case's current attribute values" },
"created_at": "2026-04-22T10:25:00Z",
"updated_at": "2026-04-22T10:30:45Z"
}
}
}
```
| Field | Description |
| ----------------------- | ------------------------------------------------------------------------------------ |
| `event` | One of `case.closed` or `case.error`. |
| `timestamp` | When the event was dispatched, in ISO 8601 UTC. |
| `data.case.id` | Unique ID of the case. Use this for deduplication. |
| `data.case.subject` | The case's human-readable subject. Never contains sensitive data. |
| `data.case.status` | `closed` or `error`. Mirrors `event` but is convenient to switch on. |
| `data.case.workflow_id` | The workflow that owns the case. Use to filter if you only care about specific ones. |
| `data.case.attributes` | The case's current attribute values, as a flat `{ key: value }` object. |
| `data.case.created_at` | When the case was created (ISO 8601 UTC). |
| `data.case.updated_at` | When the case last changed (ISO 8601 UTC). |
For workflows with restricted data handling, any case attribute marked sensitive is delivered as `"[redacted]"`;
the `subject` is always sent in the clear. See [PHI & HIPAA](/enterprise/data-protection/phi-hipaa).
## Authentication
Champ applies the authentication configured on the destination's REST API connection to every delivery:
* **Bearer** — `Authorization: Bearer `
* **Basic** — HTTP Basic credentials
* **Custom headers** — applied as-is to every request
Configure this when you create the REST API connection used as the subscription's **Connection**. See [Add a REST API integration](/integrations/rest-api).
Champ does not currently sign webhook payloads — there is no HMAC or `X-Signature` header. Use bearer-token or basic auth on the destination connection to verify that a request actually came from Champ, and restrict your handler to that credential.
## Retries
Failed deliveries retry automatically:
| Parameter | Value |
| --------------- | ---------------------------------------- |
| Max attempts | 10 |
| Backoff | Polynomially increasing between attempts |
| Success | Any HTTP status code in `200`–`299` |
| Failure | Any non-2xx response, timeout, or error |
| Connect timeout | 10 seconds |
| Read timeout | 30 seconds |
After 10 failed attempts, the event is marked `failed` and no further attempts are made. You can see which events failed and inspect the last response on the Webhooks page; there is no manual retry button today.
## Handling events
### Respond quickly
Return a 2xx response as soon as possible. Don't do heavy work inside the request handler — enqueue the payload for background processing and acknowledge immediately. A slow handler risks hitting the 30-second read timeout and being retried as a failure.
### Be idempotent
Because Champ retries until it gets a 2xx, the same payload can arrive more than once. Deduplicate on the resource id plus `event` — `data.workflow_run.id` for run events, `data.case.id` for case events (a run could fire both `completed` and `failed` in rare recovery cases — treat them as distinct keys).
### Switch on event type
```javascript theme={null}
app.post("/webhooks/champ", async (req, res) => {
const { event, data } = req.body;
// Acknowledge fast; do the real work asynchronously.
res.sendStatus(200);
// Run events carry data.workflow_run; case events carry data.case.
const resource = data.workflow_run ?? data.case;
await queue.enqueue({ event, resource });
});
```
### Filter by workflow
Subscriptions are tenant-wide. If you only care about specific workflows, filter by the payload's `workflow_id` (`data.workflow_run.workflow_id` or `data.case.workflow_id`) inside your handler rather than trying to scope the subscription itself.
# Set up webhook subscriptions
Source: https://docs.champ.ai/webhooks/subscriptions
Get notified when workflow runs complete or fail
Webhook subscriptions tell Champ to POST an event to a URL you control whenever a workflow run reaches a terminal state or a case is closed or errors. Subscriptions are **tenant-scoped** — one subscription fires for every matching workflow run or case in your organization. There is no per-workflow filtering today; filter on `workflow_id` in your handler if you only care about specific workflows.
## Prerequisites
A webhook destination is modeled as a REST API integration, so Champ can reuse its auth config when delivering events. Add one first:
1. Follow [Add a REST API integration](/integrations/rest-api).
2. Set **Base URL** to the root of your webhook receiver (e.g. `https://hooks.yourapp.com`).
3. Configure **Authentication** — this is how your handler will verify that a request actually came from Champ. See [Authentication](/webhooks/payloads#authentication).
## Create a subscription
1. Go to [**Account → Webhooks**](https://dash.champ.ai/app/account/webhooks).
2. Under **Active Webhooks**, click **Create Webhook**.
3. Fill in the fields:
The REST API integration that will receive deliveries. The full URL Champ POSTs to is the connection's **Base URL** plus the **Path** you set below.
The path on the destination (e.g. `/webhooks/champ`). Must start with `/`.
Which event to subscribe to. See [available events](#available-events).
4. Click **Create**. New subscriptions start in the `active` state and begin firing on the next qualifying run.
You can pause, resume, or delete subscriptions from the same page. Pausing stops new deliveries without clearing past events from the **Recent Events** list.
## Available events
Champ emits two families of events: **workflow run** events for any run, and **case** events for the durable
[cases](/integrations/cases-api) that workflows process.
| Event | Fires when |
| ------------------------ | ------------------------------------------------- |
| `workflow_run.completed` | A workflow run finishes successfully |
| `workflow_run.failed` | A workflow run terminates with an error |
| `case.closed` | A case transitions to its closed (resolved) state |
| `case.error` | A case transitions to its error state |
Each subscription listens for exactly one event type. To receive several events, create one subscription per event (they can point to the same path).
Case events fire only on a real transition **into** the closed or error state — re-running a case that is already in that state does not re-fire. The two families carry different payload shapes; see [Payloads & delivery](/webhooks/payloads).
## Recent Events
Every delivery attempt is recorded under **Recent Events**. Each row shows:
* **Status** — `pending`, `in_progress`, `completed`, or `failed`
* **Event type**
* **Destination URL**
* **Created** and **delivered** timestamps
* The full **payload** (expandable JSON view)
Use this to debug integrations during setup or confirm that a specific run fired. See [Payloads & delivery](/webhooks/payloads) for what to expect in your handler.