# What is Reeve? (/docs) What is Reeve? [#what-is-reeve] Reeve is **your AI business team**. Instead of an agency, a stack of contractors, and a pile of point tools, you get a **C-suite of AI agents** — a CEO, CMO, Concierge, and Engineer — that run your business: marketing, customer support, analytics, and development. You set the goals and approve the moves that matter; the team does the work. Connect the platforms you already use — Meta Ads, Shopify, Klaviyo, Google Ads, and more — and the team goes to work, all from the same shared memory so every agent knows what the others know. What your team does [#what-your-team-does] * **Marketing (CMO)** — paid ads on Meta, Google, and TikTok; email and SMS; social; brand and content, across the **DNA**, **Studio**, **Copy**, and **AdBuyer** surfaces * **Support (Concierge)** — automated customer support across chat, email, and SMS * **Analytics** — revenue, ROAS, traffic, and social performance, surfaced where you work * **Development (Engineer)** — building, testing, and shipping code * **Strategy (CEO)** — orchestrates the team and coordinates the work toward your goals How you work with it [#how-you-work-with-it] 1. **Connect your platforms** — link Meta Ads, Shopify, Klaviyo, Google Ads, and more in a few clicks 2. **Set goals and autonomy** — tell the team what to work toward and how much it can do on its own 3. **Review and approve** — Reeve confirms before spending money or publishing; you stay in control Where you use Reeve [#where-you-use-reeve] * **The cockpit at [meetreeve.com](https://meetreeve.com)** — your logged-in workspace: the dashboard, goals, agents, and the DNA, Studio, Copy, AdBuyer, Chat, and Calendar surfaces * **Inside Claude** — via the Reeve MCP, use Reeve's tools directly in your Claude conversations Get started [#get-started] Sign up and get your first result in minutes The C-suite, the cockpit, goals, agents, and autonomy Link Meta Ads, Shopify, Klaviyo, and more Create ads, copy, video, and run campaigns Access Reeve tools via the MCP inside Claude # Billing (/docs/account/billing) Billing [#billing] You manage your subscription and billing from inside the Reeve app. Find your billing settings [#find-your-billing-settings] Open **Settings → Billing** (or **Team → Billing** for an organization) to: * View and manage your subscription * Update your payment method * See invoices and payment history Monthly Spending Cap [#monthly-spending-cap] To keep your team's spend bounded, set a **Monthly Spending Cap** in your billing settings. You can set: * A **monthly cap** — the most your team may spend in a month * An optional **per-user cap** * A **reset day** — the day of the month the cap resets Reeve shows how much of the cap is left as spend climbs, so the total stays under your control no matter what the agents are working on. For per-goal limits, see [Set a goal](/docs/your-team/goals); for the full picture on keeping spend in check, see [Autonomy & approvals](/docs/your-team/autonomy-and-approvals). For plans, pricing, and what's included, see [meetreeve.com/pricing](https://meetreeve.com/pricing). # Account & Billing (/docs/account) Account & Billing [#account--billing] Everything about managing your Reeve account: your team and brand setup, your subscription and billing, and how your data is protected. Invite teammates, set roles, and manage who has access to your account Run multiple brands from a single account, each with isolated DNA, data, and surfaces Find and manage your subscription, and cap your monthly spend How your brand data is isolated, stored, and deleted # Multiple Brands (/docs/account/multi-brand) Multiple Brands [#multiple-brands] Reeve lets you run multiple brands from a single account. Each brand gets its own DNA, connected platforms, surface data, and agent context — fully isolated from your other brands. This is useful for: * **Agencies** managing multiple client brands * **Operators** running more than one e-commerce store or product line * **Holding companies** with distinct business units How It Works [#how-it-works] Each brand you add is a separate workspace within your organization: ``` Your Organization ├── Brand A ← own DNA, connectors, Copy/Studio/AdBuyer data ├── Brand B ← own DNA, connectors, Copy/Studio/AdBuyer data └── Brand C ← own DNA, connectors, Copy/Studio/AdBuyer data ``` All surfaces (DNA, Studio, Copy, AdBuyer, Chat, Calendar) scope to the active brand. Switch brands and every surface instantly reflects that brand's data. Switching Brands [#switching-brands] Use the **brand switcher** in the Reeve sidebar to move between brands: 1. Click your brand name at the top of the sidebar 2. Select a brand from the list 3. All surfaces reload with that brand's context Adding a Brand [#adding-a-brand] 1. Open the brand switcher in the sidebar 2. Click **+ Add Brand** 3. Enter the brand name and optional logo 4. Start connecting platforms for that brand Each brand starts with a clean slate — no connectors pre-configured. Set up DNA and connect platforms independently for each brand. Per-Brand Isolation [#per-brand-isolation] | Resource | Isolated per brand? | | ------------------------------------------------------- | ----------------------------- | | Brand DNA | Yes | | Platform connections (Meta Ads, Shopify, Klaviyo, etc.) | Yes | | Studio creative output | Yes | | Copy output | Yes | | AdBuyer campaigns | Yes | | Calendar | Yes | | Organization settings | Shared | | Team members | Shared (access can be scoped) | Your subscription and billing are shared across all brands in your organization. Manage them in **Settings → Billing** — see [Billing](/docs/account/billing). # Security & Data (/docs/account/security) Security & Data [#security--data] Your data in Reeve is isolated from other accounts at every level — your brand data, connected platform credentials, and AI sessions are kept completely separate from other organizations. Data Isolation [#data-isolation] Reeve enforces strict separation between accounts: * **Your brand data** — DNA, creative output, copy, campaign data, and memory — is scoped to your account and never accessible to other organizations * **Platform credentials** — OAuth tokens and API keys for your connected platforms (Meta Ads, Shopify, Klaviyo, etc.) are stored per-account and never shared * **AI sessions** — Chat sessions, agent memory, and generated content belong to your organization only Credential Storage [#credential-storage] API keys and OAuth tokens you connect are encrypted at rest. Reeve uses them only to make API calls on your behalf to your connected platforms — they are never exposed to other accounts or used for any other purpose. When you disconnect a platform in **Connect Your Data**, Reeve removes the stored credentials for that connection immediately. Data Locations [#data-locations] Reeve Cloud runs on AWS infrastructure (default region: us-east-1). Your account's data stays within the AWS region — it does not cross regional boundaries. What Reeve Stores [#what-reeve-stores] | Data type | How it's stored | | ----------------------------------------- | ----------------------------------------- | | Brand DNA | Your account's storage, not shared | | Platform credentials (OAuth, API keys) | Encrypted at rest, scoped to your account | | Generated content (copy, creative briefs) | Your account's storage | | Chat & agent session history | Your account's storage | | Connector usage metrics | Aggregated, used for usage tracking | Reeve does **not** store copies of your source platform data (Shopify orders, ad metrics, Klaviyo stats). It queries your connected platforms in real time when you ask for data. Data Deletion [#data-deletion] If you close your account: * Your data is removed from active storage within **30 days** * Backups are purged within **90 days** * Platform credentials are revoked and deleted immediately To request early deletion or export your data, contact support. Service-to-Service Security [#service-to-service-security] All internal service calls within Reeve use authenticated, encrypted channels. Session tokens are scoped and do not grant cross-account access. For questions about security or compliance, contact support via the [Help](/docs/help) section. # Teams & Members (/docs/account/teams) Teams & Members [#teams--members] Reeve uses an organization model — your account is an organization, and you can invite people to join as members. Each member gets their own login with access scoped to the roles you assign. Your Organization [#your-organization] When you sign up, Reeve creates an organization for you. You're the owner. Every brand you add and every surface you use lives inside that organization. Roles [#roles] | Role | What they can do | | ---------- | ------------------------------------------------------ | | **Owner** | Full access — settings, billing, members, all brands | | **Admin** | Manage members and brands; cannot change billing | | **Member** | Use all surfaces within the brands they're assigned to | Inviting Team Members [#inviting-team-members] To invite someone: 1. Go to **Settings → Team** in the Reeve app 2. Enter their email address 3. Select a role 4. Click **Send Invite** They'll receive an email with a link to join your organization. Once accepted, they can sign in and access the brands and surfaces their role permits. Invited users must accept the invite before they appear as active members. You can resend or revoke a pending invite from the same **Settings → Team** page. Managing Members [#managing-members] From **Settings → Team** you can: * **Change a member's role** — click their name and select a new role * **Remove a member** — revoke access immediately; their login is deactivated for your org * **Set a default brand** — each member has a default brand that loads on sign-in Member access is scoped to your organization. Removing a member from your org does not delete their Reeve account — they keep any personal accounts they may have separately. Multi-Brand Access [#multi-brand-access] If your account has multiple brands, members can be assigned access to specific brands or all brands. See [Multiple Brands](/docs/account/multi-brand) for how brand switching works. # Authentication & API keys (/docs/developers/authentication) Authentication & API keys [#authentication--api-keys] Every request to the Reeve API is authenticated with an **API key**, sent as a Bearer token. Keys are created in the Reeve app, scoped to your app/organization, shown once, and revocable. This page covers **API authentication** for calling `https://api.meetreeve.com`. For signing into the Reeve web app itself (magic link, Google, email + password), see [Getting started](/docs/getting-started/quickstart). Sending your key [#sending-your-key] Send the key in the `Authorization` header as a Bearer token: ```bash curl https://api.meetreeve.com/api/crm/v1/contacts \ -H "Authorization: Bearer rcm_your_api_key" ``` Reeve API keys are prefixed with `rcm_`. Requests without a valid key are rejected with `401`. Creating a key [#creating-a-key] Open the Reeve app and go to your app / developer settings. Create a new API key and give it a **label** so you can tell your keys apart later. **Copy the key now** — it's shown only once at creation and never displayed again. Store it somewhere safe (a secrets manager or an environment variable). ```bash export REEVE_API_KEY="rcm_..." curl https://api.meetreeve.com/api/crm/v1/stats \ -H "Authorization: Bearer $REEVE_API_KEY" ``` Treat API keys like passwords. Never commit them to version control, embed them in client-side code, or paste them into shared docs. Use environment variables or a secrets manager. How keys work [#how-keys-work] * **Hashed at rest.** Reeve stores only a SHA-256 hash of your key, never the plaintext. That's why the key is shown once — there's no way to recover it later. If you lose it, create a new one. * **Multiple keys per app.** You can issue more than one key (for example, one per environment) and label each. * **Revocable.** Revoke a key from the Reeve app at any time. Revoked keys immediately stop authenticating; other keys keep working. * **Rotating keys.** To rotate, create a new key, switch your integration to it, then revoke the old one. Request headers [#request-headers] Beyond the `Authorization` header, Reeve API requests may include these headers: | Header | Purpose | | -------------------------------- | ------------------------------------------------------------------------------------------------- | | `Authorization: Bearer rcm_…` | **Required.** Your API key. | | `X-Org-Id` | Optional. Targets a specific organization when your key has access to more than one. | | `Idempotency-Key` | Optional, on write endpoints. A unique value you supply so a retried request isn't applied twice. | | `Content-Type: application/json` | Required on requests with a JSON body. | You may also see `X-Reeve-Service-Token` referenced in the API schemas. That header is for **internal service-to-service** calls inside the Reeve backend and is **not** the developer path — public integrations authenticate with an API key as shown above. Idempotency [#idempotency] Write endpoints accept an optional `Idempotency-Key` header. Supply a unique value (a UUID is a good choice) and Reeve will ensure the operation is applied at most once even if the request is retried after a network error: ```bash curl -X POST https://api.meetreeve.com/api/crm/v1/contacts \ -H "Authorization: Bearer $REEVE_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "email": "jane@acme.com" }' ``` Errors [#errors] Authentication and validation failures use standard HTTP status codes: | Status | Meaning | | ------ | --------------------------------------------------------------- | | `401` | Missing, malformed, or revoked API key | | `403` | Authenticated, but not allowed to access the requested resource | | `422` | Request body or parameters failed validation | See the [API reference](/docs/developers/api-reference) for the per-endpoint request and response shapes. # Build an app (/docs/developers/build-an-app) Build an app [#build-an-app] Apps extend Reeve from the inside: they run **embedded in the Reeve app** and use the [SDK](/docs/developers/sdk-embeds) to reach managed AI, cloud storage, live data, and events. Users install your app and use it right where they already work. The App Platform is in **developer preview**. The flow below describes how building and shipping an app works at a high level; the exact scaffolding command, manifest fields, and review SLA may change before general availability. Confirm specifics against the current tooling; this section will expand as the App Platform reaches general availability. The shape of an app [#the-shape-of-an-app] An app is a web app you host, plus a manifest that tells Reeve how to install it: * **Your app** — any framework, served over HTTPS, loaded in an iframe inside Reeve. * **A manifest** (`reeve-manifest.json`) — identity, the permissions your app needs (e.g. AI, storage, specific data sources), and listing metadata. * **The SDK** — how your app actually calls Reeve. See [SDK & embeds](/docs/developers/sdk-embeds). From idea to listed [#from-idea-to-listed] **Scaffold** a new app and run it locally, then load it into Reeve to see it live in the iframe. **Declare permissions** in `reeve-manifest.json` — request only what your app uses. Permissions are shown to users at install time and enforced at runtime. **Build** against the SDK: generate with managed AI, persist app data in cloud storage, query connected platforms, and react to events. **Submit for review.** Apps are reviewed for quality and security before they're listed. You'll get actionable feedback; most apps need a round or two before approval. Connectors [#connectors] A "connector" in this sense is an app whose job is to bridge Reeve to an outside system — pulling data in or pushing actions out via the SDK and your own backend. The same build-and-review flow applies. Note: "connector" is also used in Reeve for the built-in data integrations users link in [Connect Your Data](/docs/connectors) (Shopify, Klaviyo, Meta Ads, and so on). Those are configured in the app, not built by developers. Reference [#reference] # HTTP or MCP — same catalog, pick your transport (/docs/developers/http-or-mcp) HTTP or MCP — same catalog, pick your transport [#http-or-mcp--same-catalog-pick-your-transport] Reeve exposes one underlying catalog of operations — CRM, credits, commerce, connects, ads, video, knowledge, and more — over two transports. They aren't two separate integrations to choose between once and commit to; pick whichever fits the calling context, per call if you like. One catalog, two transports [#one-catalog-two-transports] The [API reference](/docs/developers/api-reference) (REST/OpenAPI) and the [`/mcp` MCP server](/docs/developers/api-reference/mcp-tools) are generated from the **same source**: the public, host-key-consumable catalog defined in the Reeve backend (`api/openapi_public.py`'s per-product prefixes). Reeve's MCP tool manifest is built directly from that catalog's OpenAPI operations, curated per-substrate (renamed, described, gate-classified) rather than hand-duplicated — so a tool you can call over MCP and the REST endpoint it wraps never drift into two different shapes. ``` ┌────────────────────────┐ │ public catalog │ │ (per-product prefixes) │ └───────────┬─────────────┘ │ generated from ┌─────────────────┴─────────────────┐ ▼ ▼ REST: https://api.meetreeve.com MCP: https://api.meetreeve.com/mcp/ /api/crm/v1, /api/ads/v1, ... list_tools → describe_tools → use (+ promoted first-class tools) ``` Two auth lanes, two shapes [#two-auth-lanes-two-shapes] Both transports authenticate the same two ways Reeve authenticates everywhere — a host-app key or a user's OAuth session — but the two lanes render the catalog differently on MCP: | | REST (`/api/...`) | MCP, host-key lane | MCP, OAuth lane | | -------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | Auth | `X-Reeve-Host-Key` + `X-Reeve-Host-App` | `X-Reeve-Host-Key` + `X-Reeve-Host-App` | OAuth access token (user sign-in) | | Shape | You call the endpoint you want | **Flat** — every tool your capability grants allow, enumerated directly | **Progressive** — `get_context` → `list_tools` → `describe_tools` → `use`, plus a curated set of promoted named tools | | Best for | Server-to-server integrations, scripts, backend jobs | Agent frameworks that can hold a large static tool list (own infra, no per-session token cost) | Claude and other MCP clients where the tool list should stay small and capability-scoped per connected user | See [Authentication](/docs/developers/api-reference/authentication) for the host-key model, and [Use Reeve in Claude](/docs/use-in-claude) for the OAuth connect flow. When to pick which [#when-to-pick-which] **Call the REST API directly when:** * You're writing a backend integration, script, or scheduled job — no LLM in the loop. * You want the smallest possible request/response overhead for a known, fixed set of calls. * You need an operation that hasn't been promoted to a named MCP tool yet — every catalog operation is reachable over REST immediately. **Connect over MCP when:** * An LLM agent (Claude or another MCP client) needs to decide *which* Reeve operation to call based on a natural-language request. * You want Reeve's built-in **gating** — writes and credit-spending calls return `pending_approval` instead of executing inline, so a human confirms before anything commits. REST callers must implement their own approval step; MCP callers get it from the platform. * You're a per-user connector (claude.ai, Claude Desktop, Claude Code) where the OAuth progressive surface keeps the advertised tool list small regardless of how large the catalog grows. Both transports enforce the exact same authorization — the same capability grants, the same org/tenant scoping. Switching transports never changes what you're allowed to do, only how you ask for it. Gating is an MCP-native concept [#gating-is-an-mcp-native-concept] The REST API has no separate "propose, then approve" step — a request that's allowed either executes or is rejected. MCP's gating (`pending_approval` + `gate_id`, resolved via `resolve_gate`) exists because an MCP client is often acting on a human's behalf without the human directly typing the request — so Reeve inserts an explicit confirmation point for writes and credit-spending actions. See [Gating: writes require your approval](/docs/use-in-claude/tools#gating-writes-require-your-approval) for the full mechanics. Machine-readable references [#machine-readable-references] * REST: the full OpenAPI spec at `https://api.meetreeve.com/openapi/public/v1.json` (or per-product, e.g. `crm-v1.json`) * MCP: the live `tools/list` response from `https://api.meetreeve.com/mcp/`, documented in [MCP Tools](/docs/developers/api-reference/mcp-tools) # Developers (/docs/developers) Developers [#developers] Reeve is a hosted AI marketing and operations platform for ecommerce brands. The **Reeve API** lets you build on top of that platform — read and write the customer graph, run enrichment, and drive conversations — over plain HTTPS with a single API key. Everything below is for the **hosted product**. You don't run any Reeve infrastructure: you authenticate with an API key and call `https://api.meetreeve.com`. Base URL [#base-url] ``` https://api.meetreeve.com ``` All endpoints are versioned and namespaced by product, e.g. `/api/crm/v1/contacts`. Authentication in brief [#authentication-in-brief] Authenticate every request with a Reeve API key sent as a Bearer token: ```bash curl https://api.meetreeve.com/api/crm/v1/contacts \ -H "Authorization: Bearer rcm_your_api_key" ``` API keys are created in the Reeve app, shown once, and revocable. See [Authentication & API keys](/docs/developers/authentication) for the full model. Public products [#public-products] The API surfaces three products today. Each has its own versioned namespace and is documented in the [API reference](/docs/developers/api-reference). | Product | Namespace | What it covers | | ---------- | ---------------- | ------------------------------------------------------------------------------------- | | **CRM** | `/api/crm/v1` | Contacts, audiences, interactions, enrichment, suggestions — the Reeve customer graph | | **Memory** | `/api/enrich/v1` | Enrichment jobs, artifacts, and suggestions that derive and propose structured facts | | **Comms** | `/api/comms/v1` | Conversational tool-call surface for messaging and delivery events | **Public vs internal namespaces.** Only **CRM**, **Memory**, and **Comms** are public, documented, API-key-authenticated surfaces. Everything else in the Reeve backend (billing, connector management, Studio/AdBuyer internals, and service-to-service routes that authenticate with an internal `X-Reeve-Service-Token`) is **internal** and not part of the public API. The **Voice** product is planned and will become public when its spec lands. Reference is generated from OpenAPI [#reference-is-generated-from-openapi] The [API reference](/docs/developers/api-reference) is generated from truth-derived OpenAPI specifications — the same schemas the live API is built from — so it stays in sync with what the service actually does. The specs are committed under `openapi/` and guarded by a drift check. Explore [#explore] # SDK & embeds (/docs/developers/sdk-embeds) SDK & embeds [#sdk--embeds] There are two ways to build on Reeve: * **The HTTP API** — call `https://api.meetreeve.com` from your own backend with an [API key](/docs/developers/authentication). Best for server-to-server integrations and your own products. Start at the [API reference](/docs/developers/api-reference). * **The in-app SDK** — build an app that runs **embedded inside the Reeve app** (in an iframe) and talks to Reeve through a `postMessage` bridge. Best for tools your users open from within Reeve. This page covers the SDK. What the embedded SDK gives you [#what-the-embedded-sdk-gives-you] An embedded app loads inside Reeve and uses the SDK to reach Reeve capabilities without managing its own keys or infrastructure: | Capability | What it does | | ----------------- | ------------------------------------------------------------------------------------ | | **Managed AI** | Generate completions through Reeve's model routing — no provider API keys to manage. | | **Cloud storage** | A private key-value store scoped to the organization, for app data. | | **Live data** | Query connected platforms (Shopify, Klaviyo, ads, analytics) through one interface. | | **Events** | Subscribe to platform events and emit your own. | The SDK handles the `postMessage` protocol, authentication, and permission enforcement, so your app code is plain async JavaScript. Because it's an iframe, you can build with any framework (React, Vue, Svelte, vanilla). ``` Reeve app (parent frame) └── Your app (iframe) └── SDK ──postMessage──▶ Reeve bridge ├── Managed AI ├── Cloud storage ├── Live data └── Events ``` The embedded App Platform is in **developer preview**. The capability surface (managed AI, storage, data, events) is grounded in the SDK, but exact method signatures, permission names, and available data sources may change before general availability — confirm against the current SDK package when you build. Embedding & navigation [#embedding--navigation] An embedded app can navigate the surrounding Reeve app to its named surfaces — for example **Studio**, **Copy**, **AdBuyer**, **Chat**, or **Calendar** — or open an external URL in a new tab. Use the surface routes from the [product surfaces](/docs/generate), not legacy paths. Build one [#build-one] # Webhooks & events (/docs/developers/webhooks) Webhooks & events [#webhooks--events] Webhooks let your integration react to things that happen in Reeve without polling. You register an HTTPS endpoint for an event type; when a matching event occurs, Reeve `POST`s the event to your URL with a signature you can verify. Webhooks are currently available for **Comms delivery events**. Each subscription is scoped to your app, so you only receive events for your own activity. Other event sources will be documented here as they become public. How it works [#how-it-works] 1. You register a webhook: an HTTPS **URL** + an **event type** to subscribe to. 2. When that event occurs, Reeve sends a `POST` to your URL with the event payload as the request body. 3. Reeve signs each request with an HMAC-SHA256 signature so you can confirm it really came from Reeve. 4. Your endpoint verifies the signature and responds quickly with a `2xx`. A URL can subscribe to multiple event types; registering the same URL for the same event twice is a no-op. Comms delivery events [#comms-delivery-events] For the Comms product, you can subscribe to these delivery lifecycle events: | Event | When it fires | | -------------- | ---------------------------------------- | | `sent` | A message was handed off for delivery | | `delivered` | The provider confirmed delivery | | `bounced` | Delivery failed (hard or soft bounce) | | `opened` | The recipient opened the message | | `clicked` | The recipient clicked a link | | `complaint` | The recipient marked the message as spam | | `unsubscribed` | The recipient unsubscribed | Verifying the signature [#verifying-the-signature] Reeve signs each webhook request with an HMAC-SHA256 of a timestamp plus the raw request body, using the secret you were given when you registered the webhook. The signature is sent in a header of the form: ``` X-Reeve-Comms-Signature: t=,v1= ``` To verify, recompute `HMAC_SHA256(secret, ".")` and compare it (in constant time) to the `v1` value: ```javascript function verifyWebhook(rawBody, signatureHeader, secret) { const parts = Object.fromEntries( signatureHeader.split(',').map((kv) => kv.split('=')), ); const expected = crypto .createHmac('sha256', secret) .update(`${parts.t}.${rawBody}`) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(expected), Buffer.from(parts.v1), ); } ``` Verify the signature against the **raw, unparsed** request body. Parsing and re-serializing the JSON first can change bytes (key order, whitespace) and break verification. Reject any request whose signature doesn't match. Responding [#responding] Return a `2xx` status as soon as you've accepted the event. Do any slow work asynchronously — a slow handler can cause retries. If your endpoint returns an error or times out, Reeve may retry delivery. Registering a webhook [#registering-a-webhook] Webhook subscriptions are managed through the Reeve app / API for the Comms product. Each subscription stores your URL, the event type, and the signing secret used to verify deliveries. **Flagged:** The exact public endpoint for self-service webhook registration is part of the Comms product surface and may not yet be exposed in the committed [API reference](/docs/developers/api-reference). This page documents the verified event model, event types, and signature scheme; the registration call should be confirmed against the live API before you build against it. # Google Ads (/docs/connectors/google-ads) Google Ads Connector [#google-ads-connector] Connect your Google Ads account to pull search and display campaign performance, keyword data, quality scores, and conversion metrics into Reeve. What You Get [#what-you-get] | Data | Examples | | ------------------------ | ---------------------------------------------- | | **Campaign performance** | Impressions, clicks, CTR, cost, conversions | | **Keywords** | Search terms, quality scores, bid amounts | | **Ad groups** | Group-level metrics and status | | **Conversions** | Conversion tracking, CPA, conversion rate | | **Budget** | Daily budget, spend rate, budget utilization | | **Accounts** | Manager (MCC) and individual account discovery | Setup [#setup] Navigate to Connect Your Data [#navigate-to-connect-your-data] In the Reeve app, go to **Connect Your Data** and find **Google Ads** under Advertising. Click **Connect**. Authorize on Google [#authorize-on-google] You'll be redirected to Google's OAuth consent screen. Sign in with the Google account linked to your Google Ads account and approve access. Reeve requests read-only access to your Google Ads data. Account Discovery [#account-discovery] After authorization, Reeve: 1. Exchanges the authorization code for access + refresh tokens 2. Discovers all accessible customer accounts (Manager/MCC and individual accounts) 3. Redirects you back to the Reeve app Google Ads shows as **Connected**. Google only issues refresh tokens on the **first** authorization. If you don't receive a refresh token (e.g., you previously authorized and revoked), go to [Google Account permissions](https://myaccount.google.com/permissions), remove Reeve's access, and reconnect. Using Google Ads Data [#using-google-ads-data] In the Dashboard [#in-the-dashboard] The Google Ads connector populates the Advertising panel alongside Meta and TikTok data: * Search vs Display campaign comparison * Keyword performance and quality scores * Daily spend trends * Conversion funnel metrics Via Agent Tools [#via-agent-tools] ```typescript // Overall ad performance reeve_ads({ action: "get_performance", platform: "google" }) // Top-performing campaigns reeve_ads({ action: "get_top_performers", platform: "google" }) // Performance summary across all platforms reeve_ads({ action: "get_summary" }) ``` Example Conversations [#example-conversations] * *"How are our Google search campaigns performing this week?"* * *"What's our cost per conversion on Google Ads?"* * *"Compare our Google Ads ROAS to Meta Ads."* * *"Which keywords have the highest quality scores?"* Prerequisites [#prerequisites] * A **Google Ads account** (individual or Manager/MCC) * **Admin or Standard access** to the account * The Google account used to authorize must be linked to the Google Ads account If you use a Manager (MCC) account, Reeve discovers all child accounts automatically. Manager (MCC) Accounts [#manager-mcc-accounts] If you connect with a Manager account, Reeve sees all linked customer accounts. Dashboard data aggregates across accounts, while agent tool queries can target specific accounts. Troubleshooting [#troubleshooting] | Issue | Solution | | ----------------- | ----------------------------------------------------------------------------------------------------------- | | No refresh token | Revoke access at [myaccount.google.com/permissions](https://myaccount.google.com/permissions) and reconnect | | No accounts found | Ensure your Google account is linked to at least one Google Ads account | | Data not loading | Google Ads API can have propagation delays — wait a few minutes after connecting | Disconnecting [#disconnecting] 1. Go to **Connect Your Data** in the Reeve app → hover over the Google Ads card → click **Disconnect** 2. The refresh token is revoked and credentials are deleted You can also revoke access from [Google Account → Security → Third-party apps](https://myaccount.google.com/permissions). # Google Analytics (/docs/connectors/google-analytics) Google Analytics Connector [#google-analytics-connector] Connect Google Analytics (GA4) to pull website traffic, session data, top pages, and audience behavior into Reeve. What You Get [#what-you-get] | Data | Examples | | ------------------- | -------------------------------------------- | | **Sessions** | Total sessions, new vs returning visitors | | **Traffic sources** | Organic, paid, referral, direct breakdowns | | **Top pages** | Most-visited pages, page views, time on page | | **Audience** | Demographics, location, device type | | **Conversions** | Goal completions and conversion events | Setup [#setup] Navigate to Connect Your Data [#navigate-to-connect-your-data] In the Reeve app, go to **Connect Your Data** and find **Google Analytics** under Analytics. Click **Connect**. Enter your Google Analytics credentials [#enter-your-google-analytics-credentials] A modal opens where you paste your Google Analytics access credentials: | Field | Required | Description | | ----------------- | -------- | -------------------------------------------------------------- | | **Access Token** | Yes | Your Google OAuth access token (`ya29.…`) | | **Client ID** | Optional | OAuth client ID (`….apps.googleusercontent.com`) | | **Refresh Token** | Optional | OAuth refresh token (`1//…`) — enables automatic token renewal | Paste your credentials and click **Connect**. Connected [#connected] Reeve validates the credentials and saves them encrypted. Google Analytics shows as **Connected**. **Getting your credentials:** You can obtain a Google OAuth access token from the [Google OAuth 2.0 Playground](https://developers.google.com/oauthplayground/) or through your Google Cloud Console project. Adding a refresh token lets Reeve renew access automatically — without one, you'll need to reconnect when the access token expires (\~1 hour). Using Google Analytics Data [#using-google-analytics-data] In the Dashboard [#in-the-dashboard] The Google Analytics connector adds web traffic data to the Reeve app: * Session trends over time * Top-performing pages by traffic * Traffic source breakdown * Audience overview Via Agent Tools [#via-agent-tools] Your agents can query Google Analytics data using the `reeve_analytics` tool: ```typescript // Top pages by traffic reeve_analytics({ action: "get_top_pages", period: "30d" }) // Session summary reeve_analytics({ action: "get_sessions", period: "7d" }) // Traffic source breakdown reeve_analytics({ action: "get_sources" }) // Audience demographics reeve_analytics({ action: "get_audience" }) ``` Example Conversations [#example-conversations] * *"What are our top 10 pages this month?"* * *"Where is our traffic coming from?"* * *"How many sessions did we have last week?"* * *"What's our mobile vs desktop traffic split?"* Prerequisites [#prerequisites] * A **Google Analytics 4 (GA4) property** with data * Access to generate OAuth credentials (via Google Cloud Console or OAuth Playground) * The credentials must have read access to the GA4 property you want to connect Troubleshooting [#troubleshooting] | Issue | Solution | | -------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | Access token expired | Google access tokens expire after \~1 hour. Re-connect with a fresh token, or add a refresh token to enable automatic renewal | | No data showing | Confirm the access token has permission to read from your GA4 property | | Invalid credentials | Ensure you're using a Google OAuth access token, not an API key | Disconnecting [#disconnecting] 1. Go to **Connect Your Data** in the Reeve app 2. Hover over the Google Analytics card and click **Disconnect** 3. Credentials are deleted from Reeve Related docs [#related-docs] * [Connectors overview](/docs/connectors) — All available integrations * [Google Ads](/docs/connectors/google-ads) — Pair web analytics with ad performance data # Connectors Overview (/docs/connectors) Connect Your Data [#connect-your-data] Connectors link external platforms to Reeve. Once connected, data flows into the Reeve app, surfaces like AdBuyer and Copy can use your platform data, and analytics populate automatically. What Are Connectors? [#what-are-connectors] A connector is a secure link between Reeve and an external service. Connectors: * **Pull data** — Revenue, traffic, ad performance, email metrics * **Enable agent tools** — Agents use tools like `reeve_shopify`, `reeve_ads`, `reeve_email` to interact with connected platforms * **Feed the dashboard** — Each connected source adds data to the unified view You manage connectors from the Reeve app at **Connect Your Data** in the sidebar. Authentication Methods [#authentication-methods] Reeve uses three methods depending on the platform: | Method | How it works | Security | | --------------- | ------------------------------------------------------------------------ | ----------------------------------------------------- | | **OAuth** | Click Connect → redirected to the platform → authorize → redirected back | Tokens managed automatically, refreshed before expiry | | **API Key** | Click Connect → paste your key in a modal → validated and saved | Key encrypted before storage | | **Credentials** | Upload or paste service-account credentials (e.g. Google Analytics) | Credentials encrypted before storage | OAuth state tokens have a 10-minute TTL for CSRF protection. API keys are validated against the real API before saving — if the key is invalid, you'll see an error immediately. Documented Connectors [#documented-connectors] Commerce & Payments [#commerce--payments] | Platform | Auth | What you get | | --------------------------------------- | ----- | ---------------------------------------------- | | **[Shopify](/docs/connectors/shopify)** | OAuth | Products, orders, customers, revenue analytics | | **[Stripe](/docs/connectors/stripe)** | OAuth | Revenue, transactions, subscription data | Advertising [#advertising] | Platform | Auth | What you get | | --------------------------------------------- | ----- | ----------------------------------------------- | | **[Meta Ads](/docs/connectors/meta-ads)** | OAuth | Campaign performance, ROAS, spend, audiences | | **[Google Ads](/docs/connectors/google-ads)** | OAuth | Search/display campaigns, keywords, conversions | Marketing [#marketing] | Platform | Auth | What you get | | --------------------------------------- | ------- | --------------------------------------------- | | **[Klaviyo](/docs/connectors/klaviyo)** | API Key | Email campaigns, flows, open/click rates, SMS | Analytics [#analytics] | Platform | Auth | What you get | | --------------------------------------------------------- | ----------- | ------------------------------------ | | **[Google Analytics](/docs/connectors/google-analytics)** | Credentials | Sessions, traffic sources, top pages | Communication [#communication] | Platform | Auth | What you get | | ----------------------------------- | ----- | ------------------------------------------------ | | **[Slack](/docs/connectors/slack)** | OAuth | Bot access, channel messaging, workspace mapping | More integrations are available directly in the Reeve app under **Connect Your Data**, including TikTok Ads, PostHog, GitHub, Gorgias, Zendesk, Notion, and others. How Agents Use Connectors [#how-agents-use-connectors] Once connected, your agents access platform data through specialized tools: ```typescript // Revenue from Stripe reeve_revenue({ action: "get_summary" }) // Shopify store data reeve_shopify({ action: "get_products" }) // Ad performance from Meta reeve_ads({ action: "get_performance", platform: "meta" }) // Email campaigns from Klaviyo reeve_email({ action: "list_campaigns" }) // Web analytics from Google Analytics reeve_analytics({ action: "get_top_pages", period: "30d" }) ``` Connector data is fetched **live** when requested — Reeve doesn't store copies of your platform data. This means data is always current, but requires the external platform to be accessible. Multi-Brand Support [#multi-brand-support] In multi-brand mode, connectors are scoped per product/brand. Each brand can have its own Shopify store, ad accounts, and analytics — all managed from a single Reeve account. E-commerce store integration Facebook & Instagram advertising Search and display advertising Email and SMS marketing Payment processing & revenue Website traffic & behavior Team communication & alerts # Klaviyo (/docs/connectors/klaviyo) Klaviyo Connector [#klaviyo-connector] Connect Klaviyo to pull email and SMS campaign performance, flow analytics, segment data, and revenue attribution into Reeve. What You Get [#what-you-get] | Data | Examples | | ----------------------- | ----------------------------------------------- | | **Email campaigns** | Send count, open rate, click rate, unsubscribes | | **Flows** | Automated flow status, performance metrics | | **Segments** | Audience segments with member counts | | **SMS campaigns** | SMS sends, clicks, opt-outs | | **Revenue attribution** | Revenue generated from email/SMS | | **Templates** | Email template library | Setup [#setup] Get your Klaviyo Private API Key [#get-your-klaviyo-private-api-key] 1. Log in to [Klaviyo](https://www.klaviyo.com) 2. Click your organization name in the bottom-left corner 3. Go to **Settings → API Keys** 4. Click **Create Private API Key** 5. Name it (e.g., "Reeve Integration") 6. Set permissions to **Read-only** — this is sufficient for analytics 7. Copy the key — it starts with `pk_` Connect in Reeve [#connect-in-reeve] In the Reeve app, go to **Connect Your Data** and find **Klaviyo** under Marketing. Click **Connect**. A modal opens with one field: | Field | Value | Example | | ------------------- | ------------------------ | -------------------- | | **Private API Key** | Your Klaviyo private key | `pk_abc123def456...` | Paste your key and click **Connect**. Validated and saved [#validated-and-saved] Reeve validates the key against the Klaviyo API. If valid, the key is encrypted and stored. Klaviyo shows as **Connected**. Read-only access is all Reeve needs for analytics. You can create a key with write permissions if you want agents to create campaigns or manage flows. Using Klaviyo Data [#using-klaviyo-data] In the Dashboard [#in-the-dashboard] The Klaviyo connector adds email marketing data to the Reeve app: * Open rates and click rates over time * Campaign performance comparison * Revenue attributed to email/SMS * Flow performance overview Via Agent Tools [#via-agent-tools] Your agents use the `reeve_email` tool to interact with Klaviyo: ```typescript // Service status and connection health reeve_email({ action: "get_status" }) // Analytics overview reeve_email({ action: "get_analytics" }) // List recent campaigns reeve_email({ action: "list_campaigns" }) // Get a specific campaign reeve_email({ action: "get_campaign", campaign_id: "abc123" }) // List automated flows reeve_email({ action: "list_flows" }) // List audience segments reeve_email({ action: "list_segments" }) // List email templates reeve_email({ action: "list_templates" }) // List SMS campaigns reeve_email({ action: "list_sms_campaigns" }) ``` Campaign Management [#campaign-management] With write permissions on your API key, agents can also create and manage campaigns: ```typescript // Create a new email campaign reeve_email({ action: "create_campaign", campaign_data: { name: "Spring Sale 2026", subject: "Spring is here — 20% off everything", segment_id: "segment_123" } }) // Schedule a campaign reeve_email({ action: "schedule_campaign", campaign_id: "campaign_abc", schedule_data: { send_at: "2026-03-15T10:00:00Z" } }) ``` Example Conversations [#example-conversations] * *"What's our email open rate this month?"* * *"Which campaign had the highest click-through rate?"* * *"Show me our active automated flows."* * *"How much revenue did email generate last quarter?"* * *"Draft a campaign for our new product launch."* Troubleshooting [#troubleshooting] | Issue | Solution | | ----------------- | -------------------------------------------------------------------------------- | | Invalid API key | Ensure you're using a **Private** API key (starts with `pk_`), not a Public key | | Permission denied | Check that the key has at least Read-only access for the data types you need | | No data showing | New connections take a moment for the first data pull — refresh after 30 seconds | Disconnecting [#disconnecting] 1. Go to **Connect Your Data** in the Reeve app → hover over the Klaviyo card → click **Disconnect** 2. The API key is deleted from Reeve's encrypted store For safety, you should also revoke the API key in Klaviyo under **Settings → API Keys**. Related docs [#related-docs] * [Connectors overview](/docs/connectors) — All available integrations * [Copy & Content](/docs/generate/copy-and-content) — Generate email and SMS copy with AI # Meta Ads (/docs/connectors/meta-ads) Meta Ads Connector [#meta-ads-connector] Connect your Meta Business Suite to pull Facebook and Instagram ad campaign data, spend metrics, ROAS, and audience insights into Reeve. What You Get [#what-you-get] | Data | Examples | | ------------------------- | ---------------------------------------------- | | **Campaign performance** | Impressions, clicks, CTR, conversions | | **Ad spend** | Daily/weekly/monthly spend, budget utilization | | **ROAS** | Return on ad spend by campaign and ad set | | **Audience demographics** | Age, gender, location breakdowns | | **Creative performance** | Which ad creatives are performing best | | **Ad intelligence** | Competitor ad library research | Setup [#setup] Navigate to Connectors [#navigate-to-connectors] In the Reeve app, go to **Connect Your Data** and find **Meta Ads** under Advertising. Click **Connect**. Authorize on Facebook [#authorize-on-facebook] You'll be redirected to Facebook's authorization page. Log in with the Facebook account that has access to your ad accounts. Approve the requested permissions: * `ads_management` — Read your ad campaigns and performance data * `pages_read_engagement` — Read page data linked to ads Token Exchange [#token-exchange] After authorization, Reeve automatically: 1. Exchanges the short-lived token for a **long-lived token** (\~60 days) 2. Fetches all accessible ad accounts 3. Redirects you back to the Reeve app Meta Ads shows as **Connected** with your ad account name. Meta long-lived tokens last approximately 60 days. Reeve handles refresh automatically. If you see a disconnected state, click Connect again to re-authorize. Using Meta Ads Data [#using-meta-ads-data] **AdBuyer uses this connection.** Once Meta Ads is connected, [AdBuyer](/docs/generate/ad-campaigns) can launch and manage your Facebook and Instagram campaigns directly from Reeve. In the Dashboard [#in-the-dashboard] The Meta Ads connector populates AdBuyer with: * Total ad spend vs budget * ROAS by campaign * Top-performing ad creatives * Audience performance breakdown Via Agent Tools [#via-agent-tools] Your agents use the `reeve_ads` tool to access Meta Ads data: ```typescript // Campaign performance summary reeve_ads({ action: "get_performance", platform: "meta" }) // Top performing ads reeve_ads({ action: "get_top_performers", platform: "meta" }) // Ad intelligence — competitor analysis reeve_ads({ action: "analyze_intelligence", platform: "meta" }) // Competitor ad library search reeve_ads({ action: "get_competitor_ads", url: "competitor.com" }) // Generate new ad creative reeve_ads({ action: "generate_ad", prompt: "Summer sale for athletic wear" }) // Generate ad image reeve_ads({ action: "generate_image", prompt: "Product lifestyle shot", size: "1080x1080" }) ``` Example Conversations [#example-conversations] Ask your agent: * *"What's our ROAS on Meta this month?"* * *"Which ad creatives are converting best?"* * *"Show me our competitor's active Facebook ads."* * *"Generate 3 ad copy variants for our spring collection."* * *"How does our CPA compare to last month?"* Prerequisites [#prerequisites] To connect Meta Ads, you need: * A **Facebook Business Account** with at least one ad account * **Admin or Advertiser access** to the ad account(s) you want to connect * The Facebook account used to authorize must have access to the ad accounts If you manage ads through a Business Manager, make sure the Facebook account has been added to the Business Manager with appropriate permissions. Troubleshooting [#troubleshooting] | Issue | Solution | | -------------------- | -------------------------------------------------------------------------------------- | | No ad accounts found | Ensure your Facebook account has access to at least one ad account in Business Manager | | Token expired | Click Connect again to re-authorize — Reeve will exchange for a new long-lived token | | Permission denied | You need Admin or Advertiser role on the ad account | | Data not showing | New connections may take a few minutes for the first data pull | Disconnecting [#disconnecting] 1. Go to **Connect Your Data** in the Reeve app 2. Hover over the Meta Ads card and click **Disconnect** 3. Credentials are deleted from Reeve You can also revoke access from [Facebook Settings → Business Integrations](https://www.facebook.com/settings?tab=business_tools). Related docs [#related-docs] * [Connectors overview](/docs/connectors) — All available integrations * [Generate an ad](/docs/generate/generate-an-ad) — Create ad creatives and copy with AI * [Brand DNA](/docs/generate/brand-dna) — Competitive analysis and brand positioning # Shopify (/docs/connectors/shopify) Shopify Connector [#shopify-connector] Connect your Shopify store to pull product catalogs, order data, customer insights, and sales analytics into Reeve. What You Get [#what-you-get] Once connected, Reeve has access to: | Data | Examples | | ------------- | ------------------------------------------------------ | | **Products** | Names, prices, inventory levels, variants, collections | | **Orders** | Order history, fulfillment status, refunds | | **Customers** | Customer count, new vs returning, lifetime value | | **Revenue** | Daily/weekly/monthly revenue, average order value | | **Traffic** | Store visits, conversion rate (if analytics enabled) | | **Health** | Store status, theme info, app count | Setup [#setup] Navigate to Connectors [#navigate-to-connectors] In the Reeve app, go to **Connect Your Data** and find **Shopify** under Commerce. Click **Connect**. Enter your store domain [#enter-your-store-domain] A modal asks for your Shopify store domain. Enter your `*.myshopify.com` URL: ``` mystore.myshopify.com ``` This is your Shopify admin domain, not your custom storefront domain. Authorize on Shopify [#authorize-on-shopify] You'll be redirected to Shopify's authorization page for your store. Review the requested permissions and click **Install app**. Reeve requests read access to: * Products and collections * Orders and transactions * Customers * Store analytics * Inventory The callback includes HMAC signature verification — Shopify signs the URL and Reeve verifies it to prevent tampering. Connected [#connected] After authorization, you're redirected back to the Reeve app with Shopify showing as **Connected**. Data starts syncing immediately. Using Shopify Data [#using-shopify-data] In the Dashboard [#in-the-dashboard] The Shopify connector makes your store data available in the Reeve app: * Revenue trends (daily, weekly, monthly) * Top-selling products * Order count and average order value * Customer acquisition trends Via Agent Tools [#via-agent-tools] Your agents can query Shopify data using the `reeve_shopify` tool: ```typescript // Store overview — revenue, orders, customers at a glance reeve_shopify({ action: "get_overview" }) // Revenue breakdown for the last 30 days reeve_shopify({ action: "get_revenue", period: "30d" }) // Top products by sales reeve_shopify({ action: "get_products" }) // Customer metrics reeve_shopify({ action: "get_customers" }) // Traffic data reeve_shopify({ action: "get_traffic" }) // Geographic breakdown reeve_shopify({ action: "get_geography" }) // Store health check reeve_shopify({ action: "get_health" }) // Custom GraphQL query reeve_shopify({ action: "query", query: "{ shop { name email } }" }) ``` Example Conversations [#example-conversations] Ask your agent: * *"What were our top 5 products last month?"* * *"How's revenue trending compared to last quarter?"* * *"Show me customer acquisition by geography."* * *"What's our current inventory status for \[product]?"* Multi-Store Setup [#multi-store-setup] If you manage multiple Shopify stores, connect each one separately. In multi-brand mode, each brand maps to a different store. Agents automatically use the correct store based on the conversation context. Reeve uses Shopify's OAuth with offline access tokens. Tokens don't expire unless you uninstall the app from your Shopify admin. If you see a disconnected state, reinstall from the Connectors page. Disconnecting [#disconnecting] To remove the Shopify connection: 1. Go to **Connect Your Data** in the Reeve app 2. Hover over the Shopify card and click **Disconnect** 3. The OAuth token is revoked and credentials are deleted You can also uninstall Reeve from your Shopify admin under **Settings → Apps and sales channels**. Related docs [#related-docs] * [Connectors overview](/docs/connectors) — All available integrations * [Analytics](/docs/generate/analytics) — Ask about revenue and conversion metrics in Chat * [Multiple Brands](/docs/account/multi-brand) — Manage multiple stores from one account # Slack (/docs/connectors/slack) Slack Connector [#slack-connector] Connect Slack to Reeve so your team can interact with your AI agents directly in Slack channels and DMs. What You Get [#what-you-get] Once connected, Reeve can: | Capability | Description | | ----------------------- | ---------------------------------------------------------- | | **Respond in channels** | Mention `@Reeve` in any channel and get an answer | | **DM conversations** | Team members can DM the Reeve bot for private queries | | **Data access** | Pull business data from connected sources right into Slack | | **Proactive alerts** | Receive notifications about important business events | | **Slash commands** | Use `/reeve` to trigger specific actions | Setup [#setup] Connect via the Reeve app [#connect-via-the-reeve-app] In the Reeve app, go to **Connect Your Data** and find **Slack** under Communication. Click **Connect**. Authorize the App [#authorize-the-app] You'll be redirected to Slack's authorization page. Select the workspace you want to connect and click **Allow**. Reeve requests permission to: * Read messages in channels where it's invited * Post messages and replies * Access user profiles (for name/avatar display) * Use slash commands Invite Reeve to Channels [#invite-reeve-to-channels] After connecting, invite the Reeve bot to any channels where you want it active: 1. Open a Slack channel 2. Type `/invite @Reeve` 3. Reeve is now listening in that channel Reeve only responds when **mentioned by name** (`@Reeve`) — it won't interrupt conversations. Test It Out [#test-it-out] In a channel where Reeve is invited, try: > `@Reeve How did our revenue look last week?` > `@Reeve What are our top-performing ads?` > `@Reeve Summarize open support tickets.` Reeve pulls data from your connected platforms and responds in the thread. How It Works [#how-it-works] Channel Behavior [#channel-behavior] * **Mentions only** — Reeve responds when `@Reeve` is used, not to every message * **Threaded replies** — Responses are posted in threads to keep channels clean * **Context awareness** — Reeve maintains conversation context within a thread DM Conversations [#dm-conversations] Team members can DM the Reeve bot directly for private conversations. DMs maintain their own session history, separate from channel conversations. Slash Commands [#slash-commands] If configured, you can use `/reeve` for quick actions: | Command | What it does | | --------------- | ----------------------------- | | `/reeve status` | Show agent and gateway health | | `/reeve help` | List available commands | | `/reeve new` | Start a fresh conversation | Multiple Workspaces [#multiple-workspaces] If you manage multiple Slack workspaces, connect each one separately. In multi-brand mode, you can map each workspace to a different brand or agent. Access Control [#access-control] By default, anyone in the connected workspace can interact with Reeve. You can restrict access: * **Channel-level** — Only invite Reeve to specific channels * **DM access** — Configure which users can DM the bot * **Pairing** — Require approval for new DM conversations **Advanced configuration:** For detailed Slack setup including socket mode, webhook mode, custom app manifests, and multi-account routing, see [Connect Your Data](/docs/connectors). Disconnecting [#disconnecting] To remove the Slack connection: 1. Go to **Connect Your Data** in the Reeve app 2. Click **Disconnect** on the Slack card 3. Optionally, remove the Reeve app from your Slack workspace under **Settings → Manage Apps** # Stripe (/docs/connectors/stripe) Stripe Connector [#stripe-connector] Connect your Stripe account to pull revenue data, transaction history, and payment analytics into Reeve. What You Get [#what-you-get] | Data | Examples | | ----------------- | ------------------------------------------------ | | **Revenue** | Total revenue, MRR, daily and monthly trends | | **Transactions** | Payment history, successful charges, refunds | | **Customers** | Customer count, new vs returning, lifetime value | | **Subscriptions** | Active subscriptions, churn, plan breakdown | | **Payouts** | Payout schedule and history | Setup [#setup] Navigate to Connect Your Data [#navigate-to-connect-your-data] In the Reeve app, go to **Connect Your Data** and find **Stripe** under Payments. Click **Connect**. Authorize on Stripe [#authorize-on-stripe] You'll be redirected to Stripe's authorization page. Log in with the Stripe account that owns the data you want to connect and click **Connect**. Reeve requests read-only access to your Stripe account data. Connected [#connected] After authorization, you're redirected back to the Reeve app with Stripe showing as **Connected**. Revenue data becomes available immediately. **Alternative: API key.** If you prefer not to use OAuth, you can connect Stripe with a secret key instead. When the Connect dialog opens, switch to the **API Key** tab and paste your Stripe secret key (`sk_live_...`). The key is validated and encrypted before saving. Using Stripe Data [#using-stripe-data] In the Dashboard [#in-the-dashboard] The Stripe connector makes revenue and payment data available in the Reeve app: * Revenue trends (daily, weekly, monthly) * Transaction volume and average transaction value * Customer acquisition and lifetime value * Subscription metrics Via Agent Tools [#via-agent-tools] Your agents can query Stripe data using the `reeve_revenue` tool: ```typescript // Revenue summary reeve_revenue({ action: "get_summary" }) // Revenue over a period reeve_revenue({ action: "get_revenue", period: "30d" }) // Transaction history reeve_revenue({ action: "get_transactions" }) // Customer metrics reeve_revenue({ action: "get_customers" }) ``` Example Conversations [#example-conversations] * *"What was our revenue last month?"* * *"How many new customers did we get this week?"* * *"Show me our top revenue days this quarter."* * *"What's our current MRR?"* Prerequisites [#prerequisites] * A **Stripe account** with data you want to analyze * Access to the Stripe account (owner or administrator) If you have multiple Stripe accounts, connect the one that holds the payment data for your brand. Troubleshooting [#troubleshooting] | Issue | Solution | | -------------------- | --------------------------------------------------------------------------------------- | | No data showing | New connections may take a few minutes for the first data pull | | Authorization failed | Make sure you're logged into the correct Stripe account before clicking Connect | | API key invalid | Ensure you're using a secret key (`sk_live_...`), not a publishable key (`pk_live_...`) | Disconnecting [#disconnecting] 1. Go to **Connect Your Data** in the Reeve app 2. Hover over the Stripe card and click **Disconnect** 3. Credentials are deleted from Reeve Related docs [#related-docs] * [Connectors overview](/docs/connectors) — All available integrations * [Shopify](/docs/connectors/shopify) — Combine Shopify revenue with Stripe payment data # Ad Campaigns (/docs/generate/ad-campaigns) Ad Campaigns [#ad-campaigns] **AdBuyer** (`/adbuyer`) is Reeve's paid ad operations surface. It connects to your Meta Ads account (and Google Ads if connected) to give you a unified view of campaign performance, spend pacing, and creative results — all without leaving Reeve. This guide covers **ongoing campaign management** once a campaign is live. For creating a new ad from scratch — writing copy in Copy, building the creative in Studio, and launching — see [Generate an Ad](/docs/generate/generate-an-ad). Prerequisites [#prerequisites] AdBuyer requires at least one connected ad platform. See [Connect Meta Ads](/docs/connectors/meta-ads) to get started. What AdBuyer shows [#what-adbuyer-shows] The AdBuyer dashboard gives you: | View | What you see | | ----------------------------- | --------------------------------------------------------- | | **Total spend** | Cumulative spend vs budget across all platforms | | **ROAS** | Return on ad spend by campaign and ad set | | **Top creatives** | Which ad images and copy combinations are converting best | | **Spend trends** | Daily and weekly spend patterns | | **Multi-platform visibility** | See performance across the ad platforms you've connected | | **Campaign list** | All active and recent campaigns with key metrics | Managing campaigns [#managing-campaigns] Open **AdBuyer** from the sidebar. The dashboard loads with your current campaign data pulled live from Meta Ads (and Google Ads if connected). From AdBuyer you can: * **Monitor spend pacing** — see whether campaigns are on track against budget * **Review creative performance** — identify which ad variants are winning and which to pause * **Compare campaigns** — see ROAS, CPA, and spend side by side across your active campaigns Ad data is fetched live from Meta and Google APIs each time you open AdBuyer. Reeve does not cache copies of your campaign data — what you see is always current. Asking about campaign performance in Chat [#asking-about-campaign-performance-in-chat] For quick performance questions, open **Chat** and ask directly: * *"What's our ROAS on Meta this month?"* * *"Which campaigns are overspending their budget?"* * *"Show me our top 5 ad creatives by conversion rate."* * *"How does our CPA compare to last month?"* * *"Which ad sets should I pause based on performance?"* Chat queries your live ad data and answers in plain English, with the option to dig deeper with follow-up questions. Competitor intelligence [#competitor-intelligence] AdBuyer and DNA together give you competitor ad intelligence via the Meta Ad Library: * Search for competitor ads currently running on Facebook and Instagram * See creative formats, copy patterns, and messaging themes competitors are using * Identify which ads have been running longest (a signal of what's working) This is useful for adjusting your own creative strategy based on what's working in your category. Open **DNA** and navigate to the Ad Intelligence tab to access this. What AdBuyer does not do (yet) [#what-adbuyer-does-not-do-yet] AdBuyer is currently focused on performance visibility and campaign launching. Budget editing, audience targeting changes, and direct campaign modifications are managed in Meta Business Suite or Google Ads directly. Reeve confirms before any money-spending action. The difference between this page and "Generate an Ad" [#the-difference-between-this-page-and-generate-an-ad] | | Generate an Ad | Ad Campaigns (this page) | | ----------------- | ---------------------------- | ----------------------------------------------- | | **Goal** | Create a new ad from scratch | Monitor and manage live campaigns | | **Surfaces used** | Copy + Studio + AdBuyer | AdBuyer + Chat | | **When to use** | Starting a new campaign | Reviewing performance, pacing, creative results | Related [#related] Create a new ad — copy, creative, and launch Authorize your Facebook and Instagram ad accounts Broader analytics across all your connected data sources Query campaign performance from inside Claude # Analytics (/docs/generate/analytics) Analytics [#analytics] Reeve surfaces analytics in **Chat** (`/chat`) — a conversational interface that pulls from all your connected data sources. Ask a question in plain English and get an answer grounded in your actual numbers. You can also see structured metrics in the dashboards built into each surface (AdBuyer for ad performance, etc.). What you can ask about [#what-you-can-ask-about] The questions you can ask depend on which data sources you've connected. Common examples: | Topic | Example question | Data source needed | | -------------------- | ----------------------------------------------- | ------------------------------------- | | **Ad performance** | *"What's our ROAS on Meta this month?"* | [Meta Ads](/docs/connectors/meta-ads) | | **Ad spend** | *"How much have we spent on ads this week?"* | Meta Ads or Google Ads | | **Revenue / MRR** | *"What's our MRR right now?"* | Stripe (via Connect Your Data) | | **Customer metrics** | *"What's our average LTV and churn rate?"* | Stripe | | **Website traffic** | *"How's our site traffic trending this month?"* | GA4 or PostHog | | **Top pages** | *"What are our top 10 pages by visits?"* | GA4 or PostHog | | **Ecommerce** | *"What products are selling best this week?"* | Shopify (via Connect Your Data) | | **Email** | *"What's our email open rate this campaign?"* | Klaviyo (via Connect Your Data) | Using Chat for analytics [#using-chat-for-analytics] Open **Chat** in the sidebar. Type your question naturally — Chat knows which of your connected sources to query. Example questions to try: * *"What's our ROAS across all platforms this month?"* * *"How has revenue trended over the last quarter?"* * *"Which ad creatives are converting best?"* * *"Show me our top 5 performing campaigns."* * *"What's our bounce rate compared to last month?"* * *"How many new subscriptions did we get this week?"* * *"What does traffic look like from paid vs organic?"* Chat can follow up within a conversation — for example, ask for this month's ROAS, then ask *"How does that compare to last quarter?"* and it will track the context. Data sources that power analytics [#data-sources-that-power-analytics] Connect the relevant platforms via [Connect Your Data](/docs/connectors) to unlock the corresponding analytics: Ad spend, ROAS, creative performance, campaign metrics Search and display campaign performance Sales, orders, top products, revenue Email open rates, click rates, campaign revenue Website traffic, top pages, traffic sources MRR, LTV, churn, subscriptions Analytics data is fetched live from each connected source when you ask. Reeve does not store copies of your financial or analytics data — every answer reflects the current state of your connected platforms. Ad performance dashboards [#ad-performance-dashboards] For paid campaign metrics specifically, **AdBuyer** includes a built-in dashboard showing: * Total ad spend vs budget across platforms * ROAS by campaign * Top-performing creatives * Spend trends — daily and weekly See [Ad Campaigns](/docs/generate/ad-campaigns) for more on using AdBuyer's analytics view. Web analytics metrics [#web-analytics-metrics] When you connect GA4 or PostHog, Chat can answer questions about: * Sessions, page views, and bounce rate * Top pages by views * Traffic source breakdown (organic, direct, referral, paid) * Geographic distribution * Conversion funnels Revenue metrics [#revenue-metrics] When you connect Stripe, Chat can answer questions about: | Metric | What it means | | -------------- | ------------------------------------------------------------- | | **MRR** | Monthly Recurring Revenue — sum of active subscription values | | **ARR** | Annual Recurring Revenue (MRR × 12) | | **LTV** | Customer Lifetime Value | | **Churn rate** | Subscriptions cancelled per period | | **ARPU** | Average Revenue Per User | Related [#related] Deeper dive into AdBuyer's campaign performance view Add more data sources to unlock more analytics Query your analytics from inside Claude # Brand DNA (/docs/generate/brand-dna) Brand DNA [#brand-dna] **DNA** (`/dna`) is the foundation of everything Reeve generates. It is your brand's source of truth — voice, positioning, audience, visual identity, and product catalog — stored once so every other surface (Copy, Studio, AdBuyer, Calendar) can draw from it automatically. Without DNA, Reeve generates generic output. With DNA set up, every generated email, ad, video, and post reflects your specific brand. What DNA captures [#what-dna-captures] | Signal | What it means | | --------------------------- | ------------------------------------------------------------------ | | **Voice and tone** | How your brand communicates — formal, playful, authoritative, etc. | | **Value propositions** | The key messages your brand leads with | | **Audience** | Who your customers are and what they care about | | **Visual identity** | Logo, brand colors, and typography that Studio can apply | | **Product catalog** | Product names, descriptions, and features Copy can reference | | **Competitive positioning** | How you compare to alternatives | | **Brand health score** | A composite view of your online brand presence | Setting up DNA [#setting-up-dna] Open DNA [#open-dna] Navigate to **DNA** in the sidebar. You'll see the brand analysis interface. Enter your website URL [#enter-your-website-url] Paste in your primary website URL. Reeve analyzes the site — scraping content, extracting visual identity, and building a brand profile. This takes 2–3 minutes. Review the analysis [#review-the-analysis] Once the scan completes, review the extracted profile. You'll see your brand's voice, value propositions, audience signals, and visual identity as Reeve understood them from your site. Add competitors (optional) [#add-competitors-optional] Go to the Competitors tab and add competitor URLs. Reeve generates a side-by-side comparison — messaging, positioning, feature gaps, and ad intelligence. This powers the Insights tab and sharpens how Reeve positions your brand in generated content. Review insights [#review-insights] The Insights tab surfaces AI-generated strategic recommendations: opportunities, threats, trending topics, and specific action items prioritized by impact. DNA refreshes when you ask it to. If you update your website, rebrand, or launch new products, open DNA and run a refresh to keep the profile current. How other surfaces use DNA [#how-other-surfaces-use-dna] Once DNA is set up, every other surface picks it up automatically when you select the active brand: * **Copy** — writes in your brand voice, emphasizes your value propositions, references your products * **Studio** — applies your brand colors to templates without you specifying them * **AdBuyer** — uses your positioning when generating ad angles * **Chat** — can answer questions about your brand using the DNA profile as context The brand switcher [#the-brand-switcher] Reeve supports multiple brands from a single account. Use the brand switcher in the sidebar to move between brands — the entire app context (all surfaces, all data) changes to match the selected brand. Each brand has its own DNA profile, connected platforms, and content history. Deep-linking into Copy [#deep-linking-into-copy] From DNA, you can click on any fact, pillar, or insight to deep-link directly into **Copy** with that context pre-seeded as the prompt. This is a fast path for turning strategic insights (e.g., *"we're losing on price vs competitor X"*) directly into messaging. Example conversations in Chat [#example-conversations-in-chat] With DNA set up, you can ask Chat questions like: * *"How does our brand positioning compare to \[competitor]?"* * *"What are our strongest value propositions according to our DNA?"* * *"Refresh our brand profile — we just relaunched the website."* * *"What content opportunities are we missing based on competitor gaps?"* Related [#related] Generate on-brand copy that draws from your DNA automatically Studio uses DNA brand colors and context in creative generation Full ad workflow — copy + creative + launch Access your Brand DNA from inside Claude # Calendar (/docs/generate/calendar) Calendar [#calendar] The **Calendar** surface (`/calendar`) is Reeve's content calendar and scheduling tool. It gives you a brand-scoped view of your publishing cadence — what's scheduled, what's pending approval, and what's been published across channels. Calendar is where production output (from Copy and Studio) becomes a scheduled plan. What Calendar does [#what-calendar-does] | Feature | Description | | --------------------- | --------------------------------------------------------------------------------- | | **Content calendar** | Month/week view of scheduled content across channels | | **Brand-scoped** | Each brand has its own calendar; switch brands to see that brand's plan | | **Approval inbox** | Review and approve content before it publishes | | **Entry detail** | See the full content for any calendar entry — copy, creative, channel, and status | | **Publishing status** | Track what's live, what's scheduled, and what's in draft | Navigating Calendar [#navigating-calendar] Open Calendar [#open-calendar] Navigate to **Calendar** in the sidebar. Calendar automatically loads for your active brand. If you manage multiple brands, use the brand switcher to move between them — each brand has its own calendar. Browse the calendar [#browse-the-calendar] The calendar grid shows entries by date. Each entry represents a piece of content — a social post, email, ad creative, or other asset — with a status badge indicating where it is in the workflow. Review pending approvals [#review-pending-approvals] The **Approval Inbox** surfaces content that needs your sign-off before it publishes. Click any pending entry to see the full content, then approve or send back for revision. View entry detail [#view-entry-detail] Click any calendar entry to open the detail view. You'll see the full copy or creative, the target channel, publish time, and current status. Reeve confirms before publishing. Content moves to **Scheduled** or **Live** only after you approve it in the Approval Inbox or explicitly confirm a publish action. Calendar and the production workflow [#calendar-and-the-production-workflow] Calendar sits at the end of the production flow: 1. **Copy** generates the copy 2. **Studio** creates the visual or video 3. **Calendar** schedules and tracks the publish Content generated in Copy and Studio can be placed on the Calendar with a publish date and channel assignment. From there, the Calendar's approval step gates final publish. Multi-brand calendars [#multi-brand-calendars] If you manage multiple brands in Reeve, each has its own independent calendar. Switching brands in the sidebar switches the calendar context entirely — you see only that brand's content plan. This makes it straightforward to manage separate publishing schedules for separate brands without mixing them. Example use cases [#example-use-cases] * **Campaign planning** — lay out an entire product launch across email, social, and paid over 2–3 weeks * **Review cadence** — use the Approval Inbox as a daily checklist of what needs sign-off before it goes live * **Publishing overview** — see at a glance whether content is on track for the week Related [#related] Generate the copy that populates your calendar entries Create the visuals and video that accompany calendar entries The brand context that Calendar inherits automatically Interact with your content calendar from inside Claude # Copy & Content (/docs/generate/copy-and-content) Copy & Content [#copy--content] The **Copy** surface (`/copy`) generates on-brand text for any channel: product descriptions, press releases, email campaigns, social posts, blog content, and SMS. Everything it produces is grounded in your Brand DNA, so the voice, value propositions, and tone are consistent across every output. What Copy generates [#what-copy-generates] | Content type | Description | | ------------------------------ | ------------------------------------------------------ | | **Ad copy** | Facebook, Instagram, Google — channel-specific ad text | | **Email** | Campaign emails, promotional emails, newsletters | | **Social posts** | Instagram captions, Twitter/X posts, LinkedIn updates | | **SMS** | Short promotional and transactional messages | | **Product descriptions (PDP)** | Ecommerce product page copy | | **Blog / long-form** | Blog posts, press releases, editorial content | Single-piece generation [#single-piece-generation] Open Copy [#open-copy] Navigate to **Copy** in the sidebar. Select your active brand from the brand switcher if needed. Describe what you need [#describe-what-you-need] In the prompt panel on the left, describe the piece you want. Be specific about the offer, audience, and channel: *"Write a product description for our new insulated water bottle — targeting outdoor enthusiasts, emphasizing that it keeps drinks cold for 24 hours. Tone: confident and outdoorsy."* Select channels [#select-channels] Toggle the channels you want output for. Copy adapts the same brief into channel-appropriate formats — for example, an Instagram caption versus a Google ad headline versus an SMS blast all from the same prompt. Review the brand context [#review-the-brand-context] Before generating, Copy surfaces a preview of the brand context it will use — voice, product highlights, and key facts from DNA. Verify it looks right before clicking **Generate**. Generate and iterate [#generate-and-iterate] Click **Generate**. Results appear in the right panel, organized by channel. Use **Regenerate** to adjust tone, length, or angle without re-entering the full prompt. Campaign kits (multi-channel) [#campaign-kits-multi-channel] Switch to **Campaign** mode using the toggle at the top of the prompt panel. Kits bundle copy across all selected channels into one unified preview. This is the recommended approach for product launches, seasonal campaigns, or any time you need consistent messaging across email + social + paid simultaneously. Once a kit is generated, it lives at `/copy/kits/` — a dedicated preview page where copy and visuals stream in for all selected channels side by side. Campaign kits work best when your Brand DNA is fully set up. A complete brand profile means Copy already knows your voice, your audience, and your differentiators — you only need to describe the specific offer or angle. How DNA powers Copy [#how-dna-powers-copy] The Copy surface reads your Brand DNA automatically when you select an active brand. DNA provides: * **Voice and tone** — how your brand communicates * **Value propositions** — the key messages to emphasize * **Audience** — who the copy is for * **Product context** — features and details to draw from You don't need to re-explain your brand in every prompt. Focus the prompt on the specific offer, angle, or piece — DNA handles the rest. Example prompts [#example-prompts] * *"Write 3 email subject lines for our Black Friday sale — 40% off sitewide, urgency-focused"* * *"Product description for our bamboo cutting board — eco-conscious buyers, emphasize sustainability"* * *"Instagram caption for a new product launch — fun, aspirational, include a call to action"* * *"SMS for a flash sale ending tonight — 20% off with code FLASH20"* * *"Press release announcing our seed funding round"* Related [#related] Set up the brand source of truth that Copy reads automatically Combine Copy with Studio and AdBuyer for full ad production Generate copy from inside Claude using the Reeve MCP # Generate an Ad (/docs/generate/generate-an-ad) Generate an Ad [#generate-an-ad] Reeve's ad production workflow spans three surfaces: **Copy** writes the ad copy, **Studio** creates the image or video creative, and **AdBuyer** launches and manages the live campaign. This guide walks you through each step. Prerequisites [#prerequisites] You need at least one connected ad platform to launch. [Connect your Facebook ads](/docs/connectors/meta-ads) before starting if you haven't already. Brand DNA powers everything. If you haven't set up your brand yet, start at [Brand DNA](/docs/generate/brand-dna) — it takes about 2–3 minutes and makes every subsequent generation dramatically more on-brand. Step 1 — Write your ad copy in Copy [#step-1--write-your-ad-copy-in-copy] Open **Copy** from the sidebar (`/copy`). Select your brand [#select-your-brand] Pick the brand from the brand switcher. Copy reads your Brand DNA to write in the right voice and highlight the right value propositions. Describe the campaign [#describe-the-campaign] In the prompt panel, describe what you're promoting. Include the offer, audience, and any key message. For example: *"Summer sale, 30% off all footwear, targeting women 25–40 who follow fitness influencers."* Choose channels [#choose-channels] Select the ad channels you need — Facebook, Instagram, Google, or others. Copy generates channel-appropriate copy for each in a single run. Generate and refine [#generate-and-refine] Click **Generate**. Review the output in the results panel. Use **Regenerate** to iterate on tone, length, or angle without re-entering the prompt. **Copy kits:** For multi-channel campaigns, switch to **Campaign** mode in Copy. Kits bundle copy across all selected channels into a single preview so you can review everything in one place before moving to creative. Step 2 — Create the creative in Studio [#step-2--create-the-creative-in-studio] Open **Studio** from the sidebar (`/studio`). Choose a template or prompt [#choose-a-template-or-prompt] Studio offers a large template library including templates for product showcases, hero loops, sale promos, countdown intros, testimonials, and prompt-to-video. Pick the one that fits your campaign format, or start from a free-form prompt. Configure the template [#configure-the-template] Fill in the template fields: product image, headline, offer text, brand colors, and any other fields the template exposes. Studio pulls your brand colors from DNA automatically if your brand profile is set up. Generate and preview [#generate-and-preview] Generate the creative. Studio renders a preview — review it before downloading. For videos, the preview renders in-browser. Download or save [#download-or-save] Download the finished creative (PNG for images, MP4 for video) for use in your ad campaign. Save to the asset library for reuse. Step 3 — Launch in AdBuyer [#step-3--launch-in-adbuyer] Open **AdBuyer** from the sidebar (`/adbuyer`). AdBuyer connects to your Meta Ads account (and Google Ads, if connected) to let you launch and manage campaigns directly from Reeve. Upload the creative you made in Studio, pair it with the copy from Copy, and configure your campaign settings. Reeve will ask you to confirm before committing any ad spend. Review the campaign summary — budget, audience, placements — before approving. See [Ad Campaigns](/docs/generate/ad-campaigns) for ongoing campaign management, performance tracking, and optimization once your campaign is live. Related [#related] Authorize your Facebook and Instagram ad accounts Manage live campaigns — performance, budget, optimization Set up the brand source of truth that powers copy and creative generation Run the same ad workflow from inside Claude # Generate & Do (/docs/generate) Generate & Do [#generate--do] Reeve's six surfaces — **DNA**, **Studio**, **Copy**, **AdBuyer**, **Chat**, and **Calendar** — each own a specific job. These guides walk you through the most common tasks, end-to-end, so you can go from idea to live result as fast as possible. Write copy in Copy, create the image or video in Studio, then launch the campaign in AdBuyer — the full ad production workflow. Product descriptions, press releases, emails, social posts, and SMS — all from the Copy surface using your Brand DNA. Prompt-to-video, template-based cinematics, and livestream content from Studio. Set up your brand's source of truth so every surface generates on-brand output automatically. Ask about ROAS, MRR, traffic, and more — in Chat, backed by your connected data sources. Manage live paid campaigns in AdBuyer — performance tracking, budget pacing, and optimization. Plan, schedule, and organize your content publishing cadence with the Calendar surface. # Video (/docs/generate/video) Video [#video] The **Studio** surface (`/studio`) handles all image and video creation — from quick ad creatives to polished cinematic content. You can start from a free-form prompt or pick from a library of purpose-built templates. What you can generate [#what-you-can-generate] | Format | Template | | ------------------------ | ------------------------------------------------- | | **Prompt-to-video** | Describe the video; Studio generates it | | **Product showcase** | Feature a product with motion and copy | | **Hero loop** | Looping background video for landing pages or ads | | **Sale promo** | Animated promotional video with offer text | | **Countdown intro** | Timer-based opening for launches or flash sales | | **Testimonial** | Customer quote presented as a video card | | **Feature highlight** | Highlight a single product feature | | **Announcement reveal** | Reveal-style video for launches and news | | **Logo reveal** | Branded opener with your logo | | **Explainer steps** | Step-by-step explainer format | | **Lower third** | Motion graphic text overlay | | **Livestream** | Assets for live streaming presentations | | **Hook + content + CTA** | Three-part ad structure in one template | Generating from a prompt [#generating-from-a-prompt] Open Studio [#open-studio] Navigate to **Studio** in the sidebar. Studio opens in the editor. Choose prompt-to-video [#choose-prompt-to-video] Select the **Prompt to Video** option in the template library. Describe the video you want: subject, mood, motion style, and any on-screen text. Generate and preview [#generate-and-preview] Studio generates the video and renders a preview in-browser. Generation typically takes a few moments depending on complexity. Download [#download] Download the finished video as MP4 for use in ad campaigns, social posts, or your website. Generating from a template [#generating-from-a-template] Browse the template library [#browse-the-template-library] In Studio, open the template library and browse by category. Templates include product-focused formats, branded motion graphics, and ad-specific structures. Select and configure [#select-and-configure] Click a template to open it in the editor. Fill in the fields: product image or footage, headline, offer text, brand colors, and any other inputs the template requires. If your Brand DNA is set up, Studio pulls your brand colors automatically. Preview and refine [#preview-and-refine] Preview the output. Adjust inputs and re-generate until the result looks right. Export [#export] Download the final video. Videos export as MP4. Still frames and thumbnails export as PNG. Livestream assets [#livestream-assets] Studio includes a **Livestream** template for creating assets to use in live video presentations — branded overlays, lower thirds, and intro sequences. Navigate to the Livestream section in Studio to access these templates. For ad campaigns, pair your Studio video with copy from **Copy** and launch it directly from **AdBuyer**. See [Generate an Ad](/docs/generate/generate-an-ad) for the full workflow. Tips [#tips] * **Provide a product image for product showcases.** The more specific the visual input, the more on-brand the output. * **Use Brand DNA.** With DNA set up, Studio knows your brand colors and can produce visually consistent outputs without you specifying them each time. * **Iterate freely.** Explore different templates before committing to one. Related [#related] Use Studio creative alongside Copy text and AdBuyer launch Brand colors and context that Studio draws on automatically Trigger Studio generation from inside Claude # Core concepts (/docs/getting-started/concepts) Core concepts [#core-concepts] A handful of concepts underpin how Reeve works. Understanding them makes everything else click. Your AI team [#your-ai-team] Reeve is your **AI business team** — a C-suite of AI agents that replaces the agency stack and runs your marketing, support, analytics, and development: * **CEO** — orchestrates the team and sets strategy * **CMO** — marketing: ads, email and SMS, social, brand, and content * **Concierge** — automated customer support across chat, email, and SMS * **Engineer** — building, testing, and shipping code The agents share one persistent memory, so what one learns the others know. You run the team from the **cockpit** — your logged-in workspace. See [Your AI Team](/docs/your-team) for the full picture. Brands [#brands] Reeve is built for multi-brand operators. Each **Brand** is an isolated workspace with its own: * **DNA** — the brand's source of truth: voice, positioning, audience, and product data * **Connected platforms** — each brand has its own Shopify store, Meta ad accounts, Klaviyo list, etc. * **Creative and copy output** — generated content is scoped to the brand it was created for You can manage multiple brands from a single Reeve account. Switch between them using the brand switcher in the sidebar — the entire app context (surfaces, data, history) changes to match. The surfaces [#the-surfaces] Marketing work flows through six **surfaces**, each with a specific job: | Surface | What it does | | ------------ | ------------------------------------------------------------------------------------------------------------------------------- | | **DNA** | Brand DNA — the brand's source of truth. Feed it your brief, tone of voice, and product catalog; everything else draws from it. | | **Studio** | Image and video creation. Generate ad creatives, product visuals, and cinematic content. | | **Copy** | Email, social, blog, SMS, and ad copy. Generate on-brand text for any channel. | | **AdBuyer** | Paid ad operations. Manage and launch campaigns across your connected ad platforms. | | **Chat** | Conversational interface. Ask questions, get analysis, and orchestrate work across surfaces. | | **Calendar** | Content calendar and scheduling. Plan and organize your publishing cadence. | The surfaces work together: generate an ad image in **Studio**, write the copy in **Copy**, then launch the campaign in **AdBuyer**. Goals [#goals] Instead of running tasks one at a time, you give the team a **goal** — an outcome to work toward — with a budget and an **Approval Gate** that pauses the work when spend hits a threshold you set. The team plans the work, executes it, and reports back. See [Set a goal](/docs/your-team/goals). Autonomy & approvals [#autonomy--approvals] You choose how much the team does on its own with **Agent Mode** — **Observer** (watch and report), **Co-Pilot** (suggest, you approve), or **Auto-Pilot** (runs the show). Whatever the mode, Reeve **confirms before money-spending or publishing actions** — launching paid spend, sending an email campaign, going live. You can explore, generate, and review freely; nothing irreversible happens without your approval. See [Autonomy & approvals](/docs/your-team/autonomy-and-approvals). A goal budget's Approval Gate, the approval queue, and the account-wide Monthly Spending Cap all work together to keep spend under your control. *** The C-suite, the cockpit, goals, agents, and autonomy Link Meta Ads, Shopify, Klaviyo, and more Studio, Copy, AdBuyer — what each surface can do Access Reeve tools via the MCP inside Claude # Quickstart (/docs/getting-started/quickstart) Quickstart [#quickstart] Three steps to your first result with Reeve. Sign up at meetreeve.com [#sign-up-at-meetreevecom] Go to [meetreeve.com](https://meetreeve.com) and create your account. Sign up with email or Google — no credit card required to get started. Once in, you'll land in the Reeve app where you can see all surfaces: DNA, Studio, Copy, AdBuyer, Chat, and Calendar. Connect a platform [#connect-a-platform] Go to **Connect Your Data** in the sidebar and link your first platform. Meta Ads is a good starting point for ecommerce brands — it only takes a couple of clicks to authorize. * Click **Connect** next to Meta Ads (or Shopify, Klaviyo, Google Ads, etc.) * Follow the OAuth flow to authorize Reeve * You're redirected back once connected See [Connect Your Data](/docs/connectors) for the full connector list and per-platform setup guides, including [Meta Ads](/docs/connectors/meta-ads). Get your first result [#get-your-first-result] With a platform connected, open one of the surfaces to generate something: * **Studio** — generate an ad image or video creative for your brand * **Copy** — write ad copy, an email, a product description, or a social post * **Chat** — ask a question about your connected data (e.g. "How did our Meta ads perform last week?") Reeve will confirm before it publishes or spends — so feel free to explore. Not sure where to start? Open **Chat** and ask *"What can I do with Reeve?"* — it'll walk you through what's possible based on your connected platforms. What's next? [#whats-next] * [Core concepts](/docs/getting-started/concepts) — understand Brands, your AI team, Credits, and how Reeve's surfaces work together * [Connect Your Data](/docs/connectors) — add more platforms to unlock more capabilities * [Generate & Do](/docs/generate) — deep dive into Studio, Copy, AdBuyer, and more # FAQ (/docs/help/faq) Frequently Asked Questions [#frequently-asked-questions] About Reeve [#about-reeve] What is Reeve? [#what-is-reeve] Reeve is a hosted AI marketing and operations platform for ecommerce brands. You connect your platforms — Meta Ads, Shopify, Klaviyo, Google Ads — and an AI team works across specialized surfaces to generate creative, write copy, manage campaigns, and help you run your marketing operation. Who is Reeve for? [#who-is-reeve-for] Reeve is built for **ecommerce brands, marketers, and agencies**. If you run paid ads, send email campaigns, manage social content, or handle multiple brands, Reeve is built for your workflow. How is Reeve different from a general AI tool? [#how-is-reeve-different-from-a-general-ai-tool] General AI tools give you a chat box. Reeve gives you a purpose-built AI team wired into your actual platforms: * **Connected to your real data** — Shopify, Meta Ads, Klaviyo, and more * **Surface-specific** — Studio for creative, Copy for writing, AdBuyer for campaigns, Calendar for scheduling * **Brand-aware** — all output is grounded in your Brand DNA * **Takes action** — creates campaigns, generates ad creatives, drafts copy for review *** Pricing & Plans [#pricing--plans] How much does Reeve cost? [#how-much-does-reeve-cost] See [meetreeve.com/pricing](https://meetreeve.com/pricing) for current plans and pricing — that page is always up to date. Is there a free trial? [#is-there-a-free-trial] Visit [meetreeve.com/pricing](https://meetreeve.com/pricing) for current trial and free-tier options. Where do I manage billing? [#where-do-i-manage-billing] Open **Settings → Billing** in the app to manage your subscription and set a Monthly Spending Cap. See [Billing](/docs/account/billing). For plans and pricing, see [meetreeve.com/pricing](https://meetreeve.com/pricing). *** Features [#features] What can Reeve do? [#what-can-reeve-do] Reeve's surfaces cover the main jobs of ecommerce marketing: * **DNA** — define your brand voice, audience, and positioning * **Studio** — generate ad images and video creatives * **Copy** — write emails, social posts, ad copy, product descriptions, SMS * **AdBuyer** — launch and manage paid campaigns on Meta Ads and Google Ads * **Chat** — ask questions about your connected platform data * **Calendar** — plan and schedule your content What platforms does Reeve connect to? [#what-platforms-does-reeve-connect-to] See [Connect Your Data](/docs/connectors) for the full list. Key integrations: * **Commerce:** Shopify * **Advertising:** Meta Ads, Google Ads, TikTok Ads * **Email/SMS:** Klaviyo * **Analytics:** Google Analytics (GA4) Does Reeve work for agencies managing multiple brands? [#does-reeve-work-for-agencies-managing-multiple-brands] Yes. Reeve supports [multiple brands](/docs/account/multi-brand) — each brand gets its own DNA, connected platforms, and surface data. Manage all clients from a single account and switch between them instantly. Can multiple people on my team use Reeve? [#can-multiple-people-on-my-team-use-reeve] Yes. You can invite team members from **Settings → Team**. Each person gets their own login. See [Teams & Members](/docs/account/teams). *** Data & Privacy [#data--privacy] How is my data handled? [#how-is-my-data-handled] * **Platform data is fetched live** — Reeve queries your connected platforms in real time. It doesn't store copies of your Shopify orders or ad metrics. * **Credentials are encrypted** — API keys and OAuth tokens are encrypted at rest and scoped to your account. * **Generated content is yours** — copy, creative briefs, and AI output are stored in your account's workspace. Is my data sent to AI providers? [#is-my-data-sent-to-ai-providers] When you use a Reeve surface, your request and any relevant platform data are sent to an AI model for processing. Reeve uses reputable AI providers and does not share your data with third parties for training purposes. Can I delete my data? [#can-i-delete-my-data] Yes. If you close your account, your data is removed from active storage within 30 days and from backups within 90 days. See [Security & Data](/docs/account/security) for details, or contact support to request early deletion. *** Still have questions? [#still-have-questions] See [Support](/docs/help/support) for how to reach us. # Help & Support (/docs/help) Help & Support [#help--support] Need help? Start with self-service — most issues are covered in the guides below. Self-Service [#self-service] Common questions about Reeve, features, credits, and data Fix connection problems, auth errors, and sync issues How to reach the Reeve team for account or billing issues Set up and manage your platform connections Quick Links [#quick-links] * [Quickstart](/docs/getting-started/quickstart) — new to Reeve? Start here * [Connect Your Data](/docs/connectors) — link your platforms * [Billing](/docs/account/billing) — manage your subscription and spending cap * [Multiple Brands](/docs/account/multi-brand) — run more than one brand Service Status [#service-status] Check [status.meetreeve.com](https://status.meetreeve.com) if Reeve seems slow or unavailable — incidents are posted there in real time. # Support (/docs/help/support) Support [#support] Before You Reach Out [#before-you-reach-out] Most issues are covered in: * [FAQ](/docs/help/faq) — common questions answered * [Troubleshooting](/docs/help/troubleshooting) — step-by-step fixes for connector, sign-in, and surface issues * [status.meetreeve.com](https://status.meetreeve.com) — check for active incidents before assuming it's account-specific Contact Support [#contact-support] Visit [meetreeve.com](https://meetreeve.com) to reach the Reeve support team. You can also use the **Help** button inside the Reeve app if one is available in your plan. Include your account email and a clear description of the issue when you reach out — this helps the team respond faster. What to Include [#what-to-include] When reporting an issue, provide: 1. **What you were doing** — which surface or connector, and what action you took 2. **What happened** — the error message or unexpected behavior 3. **What you expected** — so the team can confirm whether it's a bug or expected behavior 4. **Screenshots** — especially for UI issues or error messages Service Status [#service-status] Incidents and planned maintenance are posted at [status.meetreeve.com](https://status.meetreeve.com). Check there first if Reeve seems slow or unavailable — it may already be known and in progress. # Troubleshooting (/docs/help/troubleshooting) Troubleshooting [#troubleshooting] Quick fixes for the most common issues. If yours isn't here, check the [FAQ](/docs/help/faq) or [contact support](/docs/help/support). *** Sign-In Issues [#sign-in-issues] Magic link not arriving [#magic-link-not-arriving] 1. Check your spam or junk folder 2. Try signing in with Google instead 3. Clear cookies for `meetreeve.com` and retry 4. If still stuck, [contact support](/docs/help/support) Stuck on "Signing in…" or redirected to an error page [#stuck-on-signing-in-or-redirected-to-an-error-page] 1. Clear your browser cache and cookies for `meetreeve.com` 2. Try a different browser or an incognito window 3. Disable browser extensions (some ad blockers interfere with OAuth) *** Connector Issues [#connector-issues] A connector shows "Disconnected" or "Error" [#a-connector-shows-disconnected-or-error] 1. Go to **Connect Your Data** in the Reeve sidebar and click **Reconnect** next to the affected platform 2. For OAuth connectors (Meta Ads, Google Ads, Shopify): the authorization may have expired — re-authorize to issue a fresh token 3. For API key connectors (Klaviyo, etc.): verify the key hasn't been rotated or deleted in the source platform 4. Check that the external platform account is active and not restricted Shopify data is missing or stale [#shopify-data-is-missing-or-stale] 1. Confirm the Reeve app is still installed in your Shopify admin (**Settings → Apps**) 2. Disconnect and reconnect from **Connect Your Data** 3. Shopify has a short API cache — very recent changes may take a few minutes to appear Ad data is stale [#ad-data-is-stale] Ad platforms (Meta Ads, Google Ads, TikTok Ads) typically have a **1–4 hour reporting delay** — this is normal and not a Reeve issue. Ask Reeve for a fresh report to trigger a new fetch. Klaviyo metrics not showing [#klaviyo-metrics-not-showing] 1. Verify the API key has read access to campaigns, flows, and metrics in your Klaviyo account 2. Confirm you connected with a **private** key (not a public key) 3. Disconnect and reconnect the Klaviyo connector *** Surface Issues [#surface-issues] Studio or Copy not generating [#studio-or-copy-not-generating] 1. Confirm your subscription is active in **Settings → Billing** 2. Refresh the page and try again 3. Check [status.meetreeve.com](https://status.meetreeve.com) for any ongoing incidents Chat not responding [#chat-not-responding] 1. Check [status.meetreeve.com](https://status.meetreeve.com) — AI model outages will appear there 2. Refresh the page and start a new conversation 3. If data from a specific connector is missing, check that connector's status in **Connect Your Data** AdBuyer campaign not launching [#adbuyer-campaign-not-launching] 1. Verify your Meta Ads or Google Ads connector is connected and not showing an error 2. Confirm your ad account has sufficient budget and isn't restricted by the platform 3. Check that your Brand DNA is set up — AdBuyer uses DNA for targeting context *** Account & Billing [#account--billing] A payment or subscription change isn't showing [#a-payment-or-subscription-change-isnt-showing] Payments process in real time but can occasionally lag. Wait a few minutes and refresh **Settings → Billing**. If it still hasn't updated after 10 minutes, [contact support](/docs/help/support) with your receipt. Can't access a feature I expect to have [#cant-access-a-feature-i-expect-to-have] Check **Settings → Billing** to confirm your subscription. Some features depend on your plan — see [meetreeve.com/pricing](https://meetreeve.com/pricing) for what's included. *** Still Stuck? [#still-stuck] 1. Check [status.meetreeve.com](https://status.meetreeve.com) for known issues 2. See [Support](/docs/help/support) for how to reach the team When contacting support, include your account email, the surface or connector you were using, and a description of what happened vs. what you expected. Screenshots help. # Products (/docs/products) Products [#products] Every Reeve capability has a reference page here: what it does, how to call it, and where its limits are. These pages document the product surface behind your Reeve host key (`X-Reeve-Host-Key`) — the same capabilities you toggle on a host app. Capability pages are **generated from the product registry and the Reeve.Knowledge product corpus**, so every claim traces back to a source (see each page's *Sources* section). They are published one capability at a time; browse the **Products** section in the sidebar for the pages available so far. Looking to *evaluate* a capability rather than build with it? The marketing pages at [meetreeve.com/products](https://meetreeve.com/products) cover positioning and pricing; these docs cover usage. # Agents & skills (/docs/your-team/agents-and-skills) Agents & skills [#agents--skills] Your AI team is made of **agents**. Reeve ships with a working team out of the box, and you can create and tune your own from the **Agents** area of the cockpit. Two kinds of expert [#two-kinds-of-expert] Every agent is one of two types: * **Specialist Expert** — universal expertise that works for any org. Because it isn't tied to one brand's private context, a Specialist Expert can be **published to the marketplace** for others to use. * **Domain Expert** — unique to your org. It knows your brand, context, and workflows deeply, so it stays inside your account. Roles [#roles] Agents take on roles that mirror how a real team divides work: | Role | What it does | | --------------- | ----------------------------------- | | **Manager** | Coordinates other agents and tasks | | **Coordinator** | Manages schedules and routing | | **Worker** | Executes specific tasks end-to-end | | **Assistant** | Helps with ad-hoc requests | | **Research** | Gathers and synthesizes information | Configure an agent [#configure-an-agent] Each agent has its own setup: * **Persona** — a Domain Expert's identity and instructions live in an editable `SOUL.md`, which shapes how it behaves * **Model** — choose the AI model the agent runs on * **Memory** — the agent's persistent knowledge, shared across the team so context carries between agents * **Heartbeat** — run the agent on a recurring interval so it keeps working in the background * **Team sharing** — share an agent across your org Skills [#skills] **Skills** are capabilities you enable on an agent from the cockpit's **Skills** catalog — each one extends what an agent can do. Enabling a skill injects its awareness into the agent's context, so give agents the skills they need for their job rather than everything at once. The catalog includes skills bundled with Reeve, with more available from community sources. Skills add awareness tokens to an agent's context when active. Enable what's relevant to keep agents focused and efficient. The marketplace [#the-marketplace] Specialist Experts can be **published to the marketplace**, where they become available to other Reeve users. Browse the marketplace to add proven experts to your own team instead of building everything from scratch. # Autonomy & approvals (/docs/your-team/autonomy-and-approvals) Autonomy & approvals [#autonomy--approvals] You decide how much your team does on its own. Reeve is built so the AI can do real work without doing anything irreversible behind your back: you set the autonomy level, and money-spending and publishing actions stay under your control. Agent Mode [#agent-mode] **Agent Mode** sets how much freedom the whole team has. You choose it during onboarding and can change it any time in **Settings**: | Mode | What it does | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | **Observer** | Reeve watches and reports. It hands off your accounts and doesn't make changes — the "training wheels" mode for getting comfortable. | | **Co-Pilot** | Reeve suggests moves and you approve them. The sweet spot for most teams. | | **Auto-Pilot** | Reeve runs the show — optimizes, adjusts budgets, and makes moves on its own. | Even on Auto-Pilot, the guardrails below still apply. The approval queue [#the-approval-queue] Whenever an agent wants to spend money or publish something, it surfaces the action for your approval instead of just doing it. You'll see these as: * A **goal** pausing at its **Approval Gate** and moving to **Needs Approval** — see [Set a goal](/docs/your-team/goals) * An approval inbox where you **approve** or **request changes** before something goes live * Pending actions queued in **Chat** for you to confirm This is the same human-in-the-loop pattern across the product, in the web app and [inside Claude](/docs/use-in-claude). Monthly Spending Cap [#monthly-spending-cap] For an account-wide ceiling on spend, set the **Monthly Spending Cap** in your billing settings. You can set: * A **monthly cap** — the maximum the team may spend in a month * An optional **per-user cap** * A **reset day** — the day of the month the cap resets When spend approaches the cap, Reeve shows you how much is left; the cap keeps total spend bounded no matter what the agents are working on. Trust & safety [#trust--safety] Reeve is designed to be trustworthy with real work: * **Human-in-the-loop** — money-spending and publishing actions wait for your approval; you choose how much runs automatically with Agent Mode. * **Honest about limits** — Reeve tells you what it can and can't do rather than guessing, and asks when it's unsure. * **Bounded actions** — agents work from your connected accounts and catalog and won't take actions outside what you've set up. * **Your guardrails hold** — goal budgets, approval gates, and the monthly spending cap stay in force. Start in **Observer** or **Co-Pilot** while you get a feel for how the team works, then move to **Auto-Pilot** for the areas you trust. You can change Agent Mode at any time. # The C-suite & cockpit (/docs/your-team/c-suite) The C-suite & cockpit [#the-c-suite--cockpit] Reeve gives you a **C-suite of AI agents** that operate your business. Each owns a function, the way a leadership team would, and they coordinate through shared memory so they're always working from the same picture. The C-suite [#the-c-suite] | Role | Owns | | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **CEO** | Orchestrates the team, sets strategy, and coordinates the other agents toward your goals. This is the coordinator-and-manager layer that breaks work down and routes it. | | **CMO** | Marketing: paid ads across Meta, Google, and TikTok; email and SMS; social; brand; and content. This is the work that flows through the Studio, Copy, and AdBuyer surfaces. | | **Concierge** | Customer support across chat, email, and SMS — the **Customer Concierge** that handles automated customer support and escalates when it needs you. | | **Engineer** | Developer and code work — building, testing, and shipping. | Under the hood, Reeve organizes work as a **Coordinator → Manager → Worker** hierarchy: a coordinator plans the session, managers own areas of work, and workers execute tasks. The C-suite is how that team shows up to you. The cockpit [#the-cockpit] The **cockpit** is your logged-in workspace — where you run the team day to day. It brings together: * **Dashboard** — ads, support, social, revenue, and analytics at a glance, with live metrics you can click into. Named views like the **Ad Expert**, the **Customer Concierge**, and **Social Insights** give each area a focused home. * **Agents** — create and manage your AI agents. See [Agents & skills](/docs/your-team/agents-and-skills). * **Goals** — set an outcome and let the team work toward it. See [Set a goal](/docs/your-team/goals). * **Skills** — the catalog of capabilities you enable per agent. * **Pipelines** — a live monitor of what your agents are running right now. * **Team** — members, roles and permissions, billing, API keys, and your org's agents. The daily cycle [#the-daily-cycle] Running your business with Reeve settles into a simple rhythm: 1. **Review** — open the dashboard and see what moved: ad performance, support volume, revenue, social. The team has already done the work and surfaced what matters. 2. **Approve, edit, or reject** — anything that spends money or goes live waits for your sign-off. Approve it, ask for changes, or reject it. See [Autonomy & approvals](/docs/your-team/autonomy-and-approvals). 3. **Act** — adjust a goal, nudge a campaign, or ask a question in Chat. The team picks it up from there. How much lands in your approval queue depends on the autonomy level you set — from "watch and report" to "run the show." You decide where the line is. Give the team an outcome and a budget Choose how much the team can do on its own # Set a goal (/docs/your-team/goals) Set a goal [#set-a-goal] A **goal** is how you tell the team what to work toward. Instead of running tasks one at a time, you describe an outcome — and Reeve plans the work, executes it, and reports back. You stay in control through the budget and approval gate you set up front. Create a goal [#create-a-goal] In the cockpit, open **Goals** and create a new one. Describe the outcome you want the team to drive. Common goals operators set include growing **signups**, **leads**, or a **waitlist**, increasing **MRR**, or driving **orders** — but you describe yours in your own words. A goal is made up of: * **A budget** — how much the team may spend pursuing the goal (see below) * **A deadline** — when you want the outcome by * **A report interval** — how often the team checks in * **Phases** — the steps Reeve works through to get there Set a budget and an approval gate [#set-a-budget-and-an-approval-gate] The budget is your guardrail. When you create a goal you can set: * **Token Limit** — a ceiling on the AI work the goal can consume * **Dollar Limit** — a hard cap on spend for the goal * **Approval Gate ($)** — a spend threshold that pauses the goal for your review The **Approval Gate** is the important one for staying in control of money. When the goal's spend reaches the gate, the goal stops and moves to **Needs Approval** rather than continuing on its own. Nothing further is spent until you approve. The Approval Gate is per-goal. For an account-wide ceiling, set the **Monthly Spending Cap** in your billing settings — see [Autonomy & approvals](/docs/your-team/autonomy-and-approvals). Track progress [#track-progress] Once a goal is running, you can follow it from the Goals view. A goal moves through these states: | Status | Meaning | | ------------------ | ------------------------------------------------------- | | **Active** | The team is working on it now | | **Pending** | Queued, not yet started | | **Paused** | Temporarily stopped | | **Needs Approval** | Paused at the approval gate — waiting for your sign-off | | **Completed** | The outcome was reached | | **Failed** | The goal could not be completed | You choose how often the team reports in — options range from every 30 minutes up to hourly, every few hours, or once a day. Open any goal to see its phases and where the work stands. Approve when it pauses [#approve-when-it-pauses] When a goal hits its approval gate and moves to **Needs Approval**, review what the team wants to do next. Approve it to let the work continue, or adjust the budget and goal first. This is the same human-in-the-loop pattern Reeve uses everywhere money or publishing is involved — see [Autonomy & approvals](/docs/your-team/autonomy-and-approvals). # Your AI Team (/docs/your-team) Your AI Team [#your-ai-team] Reeve is **your AI business team**. Instead of stitching together an agency, a handful of contractors, and a stack of point tools, you get a team of AI agents that run your marketing, customer support, analytics, and development — all connected, all working from the same shared memory. You don't operate the agents one prompt at a time. You **set goals**, choose **how much autonomy** the team has, and **approve** the moves that spend money or go live. The team does the work; you stay in control. Replace your agency stack [#replace-your-agency-stack] A typical brand pays for a media buyer, a creative shop, an email agency, a support desk, and an analyst — each in their own tool, none of them talking to each other. Reeve replaces that stack with a single team: * **Marketing** — paid ads (Meta, Google, TikTok), email and SMS, social, brand, and content * **Support** — automated customer support across chat, email, and SMS * **Analytics** — revenue, ROAS, traffic, and social performance, surfaced where you work * **Development** — building, testing, and shipping code Because every agent shares the same persistent memory, what one agent learns, the others know. Your ads agent knows what your support agent just heard about a product issue — in the same session. The cockpit [#the-cockpit] You run the team from the **cockpit** — your logged-in workspace at [meetreeve.com](https://meetreeve.com). The cockpit is where you see the business at a glance, set goals, manage agents, and approve actions. The creative surfaces — DNA, Studio, Copy, and AdBuyer — are part of what the team does from here. You can also work with your team from inside Claude via the Reeve MCP. See [Use Reeve in Claude](/docs/use-in-claude). Explore the team [#explore-the-team] Meet the CEO, CMO, Concierge, and Engineer — and how you run the business from the cockpit Give the team an outcome to work toward, set a budget and an approval gate, and track progress Create and manage agents, give them skills, and publish to the marketplace Choose how much the team can do on its own — and what stays under your sign-off # Connect Reeve to Claude (/docs/use-in-claude/connect) Connect Reeve to Claude [#connect-reeve-to-claude] Reeve exposes a remote MCP server at: ``` https://api.meetreeve.com/mcp/ ``` Add this URL as a custom MCP connector in Claude. Authentication uses OAuth — you'll sign in to your Reeve account once, and Claude handles the token from there. No manual token pasting required. **Already connected to `/mcp/claude`?** That connection keeps working exactly as it does today — it's the legacy 3-tool mount (`chat`, `get_context`, `resolve_gate`) and isn't going away. You don't need to reconnect. This page's instructions are for **new** connections, which should point at `/mcp` to get the fuller tool surface described in [The tools](/docs/use-in-claude/tools). Open Claude Desktop settings [#open-claude-desktop-settings] Go to **Settings → Developer** (or **Integrations**, depending on your version) and find the MCP / custom connectors section. Add a custom connector [#add-a-custom-connector] Click **Add custom connector** (or **Edit config** to add to `claude_desktop_config.json` directly). Add the Reeve server URL: ```json { "mcpServers": { "reeve": { "url": "https://api.meetreeve.com/mcp/" } } } ``` Save the config and restart Claude Desktop. Authorize Reeve [#authorize-reeve] When you first use a Reeve tool, Claude Desktop will prompt you to sign in to Reeve. Complete the OAuth flow in your browser — you'll be redirected back once connected. For the current Claude Desktop connector UI, see [Anthropic's MCP documentation](https://docs.anthropic.com/en/docs/agents-and-tools/mcp). Open Integrations [#open-integrations] In [claude.ai](https://claude.ai), go to **Settings → Integrations** (or **Connectors**). Add a custom connector [#add-a-custom-connector-1] Click **Add custom connector** and paste the Reeve MCP server URL: ``` https://api.meetreeve.com/mcp/ ``` Sign in to Reeve [#sign-in-to-reeve] Click **Connect** (or **Authorize**). You'll be taken to Reeve's OAuth sign-in. Log in with your Reeve account and approve the connection. You're redirected back to claude.ai once authorized. The exact connector UI may vary as Anthropic updates claude.ai. See [Anthropic's connector documentation](https://docs.anthropic.com/en/docs/agents-and-tools/mcp) for the current steps. Run the following command in your terminal: ```bash claude mcp add --transport http reeve https://api.meetreeve.com/mcp/ ``` This registers the Reeve MCP server with Claude Code. On first use, you'll be prompted to complete the OAuth sign-in flow in your browser. See [Claude Code MCP documentation](https://docs.anthropic.com/en/docs/claude-code/mcp) for more on managing MCP servers in Claude Code. What happens after you connect [#what-happens-after-you-connect] Once connected and authorized, Reeve appears as an available integration in Claude. You can start a conversation and ask Claude to work with your Reeve workspace — no extra setup needed. Claude discovers what's available by calling `get_context`, then browses and calls tools as your request needs them (see [The tools](/docs/use-in-claude/tools) for how that discovery works). To verify the connection is working, ask Claude: *"What brands do I have in Reeve?"* — Claude will call `get_context` and return your workspace details. Advanced: programmatic access [#advanced-programmatic-access] For programmatic or substrate consumers (e.g., automated workflows not tied to a user account), the Reeve MCP also supports service-level authentication via an `X-Reeve-Host-Key` header, on both `/mcp` and `/mcp/claude`. A host-key connection sees a flat enumeration of every tool its capability grants allow, rather than the progressive get\_context → list\_tools → describe\_tools → use flow OAuth connections use. This is an internal/advanced path — contact [support](/docs/help/support) if you need it. # Example prompts (/docs/use-in-claude/examples) Example prompts [#example-prompts] Once Reeve is connected to Claude, you can ask Claude to work with your Reeve workspace in natural language. These examples show what to ask and what to expect — organized by the [departments](/docs/use-in-claude/tools#promoted-tools-called-by-name) your connection has access to. You'll only see results for departments your connection is actually granted — if a prompt below doesn't work, that department likely isn't enabled for your app. CRM [#crm] **Find contacts by tag** > *"Show me our CRM contacts tagged 'vip' who haven't ordered in 30 days."* Claude calls `crm_list_contacts` with a tag filter and returns the matching contacts. **Check CRM health** > *"How many active contacts do we have, and how many interactions in the last 30 days?"* Claude calls `crm_list_stats` for the aggregate counts. Credits [#credits] **Check your balance** > *"What's our current Reeve credit balance?"* Claude calls `credits_get_balance`. **Review recent spend** > *"Show me our last 20 credit transactions."* Claude calls `credits_get_ledger`, paginated newest-first. Ads (adbuyer) [#ads-adbuyer] **Check last week's ROAS** > *"What was our ROAS last week across Meta Ads?"* Claude calls `ads_list_insights` with `provider: "meta_ads"` and a relative date window. No need to open AdBuyer. **Compare campaign performance** > *"How did our two active Meta campaigns perform against each other this month? Give me spend, ROAS, and CPC."* Claude calls `ads_list_campaigns` to find the campaigns, then `ads_list_insights` per campaign. **See which platforms are connected** > *"Which ad platforms do we have connected?"* Claude calls `ads_list_providers`. Video (studio) [#video-studio] **Check a render's status** > *"Is render abc-123 done yet?"* Claude calls `video_get_renders` with the render id. **Browse recent projects** > *"What Studio video projects have we worked on recently?"* Claude calls `video_list_projects`, newest-updated first. Connections [#connections] **See what's connected** > *"What platforms does our Reeve workspace have connected — Meta, Klaviyo, Shopify?"* Claude calls `connects_list_connections`. Commerce [#commerce] **Look up pricing** > *"What's our current rate card for the Studio app?"* Claude calls `commerce_get_rate_card`. Knowledge [#knowledge] **Ask Reeve how to do something** > *"How do I set up a new ad campaign in Reeve?"* Claude calls `reeve_howto`, which searches Reeve's knowledge base and returns guidance — or an honest "nothing found" if there's no matching card, rather than guessing. Drafting copy and creative [#drafting-copy-and-creative] **Draft a Black Friday launch email** > *"Ask Reeve to draft a Black Friday launch email for our apparel brand. Subject line and body, 30% off sitewide."* This is orchestrated, creative work, so Claude routes it through `chat` rather than a direct tool. Reeve draws on your brand's DNA — voice, tone, and product catalog — to write on-brand copy. Claude returns the draft for you to review before you do anything with it. **Generate ad concepts** > *"Have Reeve generate 3 ad concepts for our new running shoe launch. Include hook, body copy, and a CTA for each."* Scope to a specific brand [#scope-to-a-specific-brand] If you manage multiple brands in Reeve, you can target a specific one: > *"Ask Reeve about my 'Coastal Threads' brand — what campaigns are currently running?"* Claude calls `get_context` to find the brand id for "Coastal Threads", then passes it to `chat` so Reeve responds in that brand's context. Something not listed above [#something-not-listed-above] Reeve exposes far more than the tools with example prompts here — CRM writes, comms, enrichment, database, maps, booking, and more. If you ask for something whose direct tool isn't promoted to a name yet, Claude falls back to browsing: > *"What Reeve tools do you have for the 'comms' department?"* Claude calls `list_tools(department: "comms")` to see what's there, `describe_tools` on the one that fits, then `use` to call it — the same discovery flow, just one step longer than a promoted tool. Approving a gated action [#approving-a-gated-action] Some actions — like launching paid spend or publishing a campaign — require your explicit approval. Here's what that flow looks like: > *"Ask Reeve to launch our Black Friday Meta campaign."* Claude sends this to Reeve via `chat`. Reeve prepares the campaign and returns a gate: > **Claude:** Reeve is ready to launch the "Black Friday 2024" campaign on Meta with a $500/day budget. This will start spending immediately. Do you want to approve? You confirm: > *"Yes, approve it."* Claude calls `resolve_gate` with `decision: "approve"` and Reeve confirms the launch. Approving a gate commits the action. Review the budget, targeting, and schedule before confirming. Continue a conversation [#continue-a-conversation] Reeve maintains context within a `chat` thread. You can follow up naturally: > *"Ask Reeve to draft a subject line for that Black Friday email."* > *"Now make it punchier — under 8 words."* > *"What's the open rate for our last email with a similar subject?"* Claude passes the `thread_id` across turns so Reeve knows you're continuing the same conversation. What Reeve can't do from Claude [#what-reeve-cant-do-from-claude] Reeve's MCP gives you access to the same capabilities as the Chat surface in the web app, plus growing direct-tool coverage. Actions that require the full visual interfaces — like building out a Studio creative with image uploads, or managing the Calendar drag-and-drop — are best done directly in the [Reeve app](https://meetreeve.com). # Use Reeve in Claude (/docs/use-in-claude) Use Reeve in Claude [#use-reeve-in-claude] Connect Reeve to Claude and talk to your marketing workspace without leaving your Claude conversation. Ask Reeve to draft copy, pull CRM contacts, check ad performance, browse render presets, or run campaigns — all through natural conversation. What this gives you [#what-this-gives-you] Once connected, Claude gets two ways to work with Reeve: the **brain** (`chat`) for orchestrated, creative work, and a growing set of **direct tools** for fast, deterministic reads — CRM, credits, commerce, connections, ads, video, and knowledge. | What you can do | How | | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | Draft copy, emails, and ad creatives | Ask Claude; it sends the request to Reeve's `chat` | | Look up CRM contacts, ad performance, credit balance, render status | Claude calls a direct tool (e.g. `crm_list_contacts`, `ads_list_insights`) | | Discover what else is possible | Claude calls `list_tools`/`describe_tools` to browse and inspect tools it hasn't used yet | | Scope a conversation to a specific brand | Claude calls `get_context` to list your brands, then passes the id | | Launch paid spend or publish content | Reeve returns a **gate** — you approve it before anything happens | Gates: spending and publishing require your approval [#gates-spending-and-publishing-require-your-approval] Reeve never spends your budget or publishes content without explicit approval. When a write action is triggered, Reeve returns a **gate** — a pending action (`pending_approval` with a `gate_id`) that requires your sign-off. Claude will surface the gate details and ask you to confirm before calling `resolve_gate` to approve it. Reads (looking things up) never gate — they run immediately. This mirrors how Reeve works in the web app: every significant action asks for your approval before it commits. Works with your brands [#works-with-your-brands] If you manage multiple brands in Reeve, you can scope any conversation to a specific brand. Claude calls `get_context` to fetch your brand list, then passes the brand's `id` to `chat`. Without a `brand_id`, Reeve defaults to your primary workspace. **Existing connector?** If you connected Reeve to Claude before this surface shipped, your connection keeps working unchanged at the legacy `/mcp/claude` endpoint — see [Connect Reeve to Claude](/docs/use-in-claude/connect) for the distinction. New connections should use `/mcp`. *** Add the MCP server in Claude Desktop, claude.ai, or Claude Code How Claude discovers and calls Reeve's tools, and how gating works Realistic things to ask Claude once connected, by department # The tools (/docs/use-in-claude/tools) The tools [#the-tools] You don't call these directly — you talk to Claude naturally and it calls them on your behalf. This page explains what's available and how Claude finds and uses it, so you know what to expect. This describes the **unified `/mcp` surface** (recommended for new connections). If you're still on the legacy `/mcp/claude` connector, see the note in [Connect Reeve to Claude](/docs/use-in-claude/connect) — it exposes only `chat`, `get_context`, and `resolve_gate`, documented in the [MCP Tools reference](/docs/developers/api-reference/mcp-tools#mcpclaude--legacy-existing-connectors-only). Two kinds of tools [#two-kinds-of-tools] **`chat`** is the brain — send it a natural-language request and Reeve's agent figures out what to do, drawing on your brand's voice, catalog, and connected platforms. Good for anything orchestrated or creative: drafting copy, generating ad concepts, multi-step campaign work. **Direct tools** are fast, deterministic reads — list your CRM contacts, check an ad campaign's performance, look up your credit balance. Claude calls these directly instead of routing through `chat` when a request is a straightforward lookup. How Claude discovers what's available [#how-claude-discovers-whats-available] Because Reeve's tool catalog is large, Claude doesn't see every tool up front. It discovers what it needs in three steps: ``` get_context() → your orgs, brands, host_apps, and available `departments` list_tools(department?) → tool names + one-line descriptions, optionally narrowed to one department describe_tools(names) → full input schemas for up to 10 tools, right before calling them ``` Then it calls a tool one of two ways: * **By name directly**, for a curated set of high-traffic reads that are "promoted" to first-class tools (see below) — e.g. `crm_list_contacts(...)`. * **Through `use(tool, args)`**, for everything else in the catalog — Claude passes the tool name and a JSON `args` object matching the schema `describe_tools` returned. You'll typically never see this happen — Claude runs `get_context` at the start of a session and calls `list_tools`/`describe_tools` on demand as your requests need tools it hasn't used yet. Promoted tools: called by name [#promoted-tools-called-by-name] A curated subset of high-traffic **read-only** tools are promoted to first-class named tools, so you (or an MCP client's "always allow" setting) can scope trust to individual tools instead of one blanket grant on `use`. These are visible only for departments your connection has been granted: | Department | Promoted tools | | ----------- | ---------------------------------------------------------------- | | `crm` | `crm_list_contacts`, `crm_list_stats` | | `credits` | `credits_get_balance`, `credits_get_ledger` | | `commerce` | `commerce_get_rate_card` | | `connects` | `connects_list_connections`, `connects_list_capabilities` | | `adbuyer` | `ads_list_providers`, `ads_list_campaigns`, `ads_list_insights` | | `studio` | `video_list_presets`, `video_list_projects`, `video_get_renders` | | `knowledge` | `knowledge_list_corpora`, `knowledge_list_ask` | Everything else Reeve exposes (CRM writes, comms, enrichment, database, maps, booking, and more) is still reachable through `list_tools` → `describe_tools` → `use` — it's just not promoted to a named tool yet. See the full schemas in the [MCP Tools reference](/docs/developers/api-reference/mcp-tools). The brain tools [#the-brain-tools] Four tools are always available, regardless of which departments your connection is granted: chat [#chat] ``` chat(message, thread_id?, host_app?, brand_id?) ``` Talk to your Reeve workspace. Pass the returned `thread_id` on follow-ups to keep one conversation going. `brand_id` (from `get_context`) scopes the conversation to a specific brand. get_context [#get_context] ``` get_context() ``` Returns your org(s), user info, brand list, and the `departments` of tools available to this connection. Claude calls this first, every session. reeve_howto [#reeve_howto] ``` reeve_howto(task, host_app?) ``` Semantic help: describe a goal in plain language and get back guidance on which Reeve tools accomplish it, drawn from Reeve's own knowledge base. Returns an honest empty result (never a fabricated answer) when nothing matches. resolve_gate [#resolve_gate] ``` resolve_gate(gate_id, decision, thread_id?) ``` Approve or cancel a pending gated action — see **Gating**, below. `decision` is `"approve"` or `"cancel"`. `thread_id` is only needed to resolve a gate that came back inside a `chat` reply; a gate returned directly by a tool call resolves without one. Gating: writes require your approval [#gating-writes-require-your-approval] Reeve draws a hard line between **reads** and **writes**: * **Reads** — looking things up (contacts, campaigns, balances, renders, and so on) — dispatch immediately. No approval needed. * **Writes and credit-spending actions** — publishing, launching spend, creating records — never execute inline. The tool call instead returns: ```json { "status": "pending_approval", "gate_id": "..." } ``` Claude surfaces the pending action and waits for you to explicitly confirm, then calls `resolve_gate(gate_id, "approve")` to actually run it — or `resolve_gate(gate_id, "cancel")` to drop it. Approving a gate commits the action — for example, it may launch ad spend or publish content. Review the details Claude surfaces before confirming. Credit-spending tools (renders, AI completions, and similar) currently gate the same way writes do, pending a finer-grained cost-threshold policy — so today, every non-read action asks for your approval rather than only the more expensive ones. How it works together [#how-it-works-together] 1. You ask Claude something naturally 2. Claude calls `get_context` (once per session) to see what's available 3. For a read, Claude calls the tool directly (by name if promoted, or via `list_tools`/`describe_tools`/`use` otherwise) and returns the result 4. For a write, Reeve returns `pending_approval` — Claude explains what it's about to do and waits for you to say yes 5. You confirm → Claude calls `resolve_gate(gate_id, "approve")` → Reeve completes the action 6. For orchestrated or creative work, Claude uses `chat` instead of a direct tool, reusing `thread_id` across a conversation # Authentication (/docs/developers/api-reference/authentication) Reeve API requests authenticate with a **host-app key** — a server-side secret issued to your application. Headers [#headers] | Header | Required | Value | | ------------------ | --------- | ------------------------------------------------------ | | `X-Reeve-Host-Key` | yes | Your host-app key (begins with `rcm_`) | | `X-Reeve-Host-App` | yes | Your host-app identifier | | `X-Org-Id` | sometimes | Target organization, when your app spans multiple orgs | ```bash curl https://api.meetreeve.com/api/crm/v1/contacts \ -H "X-Reeve-Host-Key: rcm_your_key_here" \ -H "X-Reeve-Host-App: your-app" ``` Capabilities [#capabilities] Each product is a **capability** granted to your host-app (`crm`, `memory`, `comms`, `channels`, `enrich`, `voice`). Calling a product your app hasn't been granted returns **403**. You pick which products to enable per app — pay only for what you use. Getting a key [#getting-a-key] Host-app keys are provisioned through the Reeve developer platform. Keep keys server-side; they carry the privileges of every capability granted to the app. > Webhook endpoints (provider → Reeve) and health probes are intentionally **not** part of this reference — they aren't called by your app. # Errors (/docs/developers/api-reference/errors) The API uses conventional HTTP status codes. `2xx` means success; `4xx` means the request was rejected; `5xx` means a server-side error. Status codes [#status-codes] | Status | Meaning | Common cause | | ------ | -------------------- | ------------------------------------------------------------ | | `400` | Bad request | Malformed body or invalid parameters | | `401` | Unauthenticated | Missing or invalid `X-Reeve-Host-Key` | | `403` | Forbidden | Host-app lacks the capability for this product, or wrong org | | `404` | Not found | Resource (or its parent) does not exist | | `409` | Conflict | Idempotency or resource-state conflict | | `422` | Unprocessable entity | Request validation failed | | `429` | Rate limited | Too many requests — back off and retry | | `5xx` | Server error | Transient — retry with backoff | Error shape [#error-shape] CRM v1 and other products return a structured error body: ```json { "error": { "code": "org_forbidden", "message": "user is not a member of the requested org", "retryable": false } } ``` `retryable` indicates whether the same request may succeed on a later attempt. Per-operation reference pages list the specific responses each endpoint can return. # API Reference (/docs/developers/api-reference) The Reeve API lets your app consume individual Reeve products à la carte over HTTP. This reference is generated directly from the live API and is verified against it on every deploy, so it never drifts from what the server actually does. Base URL [#base-url] ``` https://api.meetreeve.com ``` Products [#products] | Product | Base path | What it does | | ------------ | ------------------ | ------------------------------------------------------------- | | **CRM** | `/api/crm/v1` | Contacts, lifecycle, audiences | | **Memory** | `/api/memory/v1` | Namespaced hybrid (vector + lexical) memory + knowledge graph | | **Comms** | `/v1/comms` | Transactional email & SMS | | **Channels** | `/api/channels/v1` | Slack and other channel messaging | | **Enrich** | `/api/enrich/v1` | Recipe-driven enrichment | | **Voice** | `/api/voice/v1` | Voice agents, calls, and agent tools | Authentication [#authentication] Every request authenticates with a **host-app key** in the `X-Reeve-Host-Key` header, plus your `X-Reeve-Host-App` identifier. See [Authentication](/docs/developers/api-reference/authentication). Machine-readable spec [#machine-readable-spec] The full, filtered OpenAPI spec is published for tools and LLMs: ``` https://api.meetreeve.com/openapi/public/v1.json ``` Per-product specs are at `https://api.meetreeve.com/openapi/public/{product}-v1.json` (e.g. `crm-v1.json`). Drop either into your LLM to explore the whole API. > Every page in this reference is also available as plain markdown — request it with `Accept: text/markdown`, or see [llms.txt](/llms.txt) / [llms-full.txt](/llms-full.txt). # MCP Tools (/docs/developers/api-reference/mcp-tools) Reeve exposes agent tools over the Model Context Protocol at two mounts. Generated from the live `tools/list` of each. See [HTTP or MCP](/docs/developers/http-or-mcp) for how this relates to the REST API, and [Use Reeve in Claude](/docs/use-in-claude) for the end-user connect flow. /mcp — unified, progressive surface (recommended) [#mcp--unified-progressive-surface-recommended] The brain tools (`chat`, `get_context`, `resolve_gate`, `reeve_howto`) and meta tools (`list_tools`, `describe_tools`, `use`) are always present. Promoted tools are a capability-filtered subset — you'll only see the ones your connection is granted. get_context [#get_context] **brain tool — listed on both the flat and progressive surfaces, never capability-filtered** Who you are in Reeve: orgs, user, brands, host\_apps — plus the `departments` of tools available here. Start every session with this. *No parameters.* list_tools [#list_tools] **meta tool — progressive surface only** List available Reeve tools (name + one-line description). Pass `department` (from get\_context) to narrow; omit for all. | Name | Type | Required | Description | | ------------ | ------ | -------- | ----------- | | `department` | string | no | | describe_tools [#describe_tools] **meta tool — progressive surface only** Full input schemas for up to 10 tools you're about to call via `use`. | Name | Type | Required | Description | | ------- | ----- | -------- | ----------- | | `names` | array | yes | | use [#use] **meta tool — progressive surface only** Call a Reeve tool by name with a JSON `args` object matching its schema (see describe\_tools). Optional `host_app` picks which of your org's host apps the call acts as (default: oldest granted). | Name | Type | Required | Description | | ---------- | ------ | -------- | ----------- | | `tool` | string | yes | | | `args` | object | no | | | `host_app` | string | no | | chat [#chat] **brain tool — listed on both the flat and progressive surfaces, never capability-filtered** Talk to the Reeve agent (the brain): renders, brand questions, campaign work. Pass the returned thread\_id on follow-ups. Prefer direct tools for deterministic reads; chat for orchestrated work. | Name | Type | Required | Description | | ----------- | ------ | -------- | ----------- | | `message` | string | yes | | | `thread_id` | string | no | | | `host_app` | string | no | | | `brand_id` | string | no | | resolve_gate [#resolve_gate] **brain tool — listed on both the flat and progressive surfaces, never capability-filtered** Approve or cancel a pending gated action from a previous reply. Only call after the user explicitly confirmed. decision: approve|cancel. `thread_id` is only needed for a chat-originated gate — a gate returned by a direct tool call (`gate_id` from a `pending_approval` result) resolves without one. | Name | Type | Required | Description | | ----------- | ------ | -------- | ----------- | | `thread_id` | string | no | | | `gate_id` | string | yes | | | `decision` | string | yes | | reeve_howto [#reeve_howto] **brain tool — listed on both the flat and progressive surfaces, never capability-filtered** How to accomplish a stated goal with Reeve's tools — semantic help over the knowledge base (`knowledge_list_ask` with a job description). Returns matching guidance cards, or an honest empty result with a next-step hint when nothing matches — never a fabricated answer. Once you know which tool to call, use describe\_tools for its exact schema. | Name | Type | Required | Description | | ---------- | ------ | -------- | ----------- | | `task` | string | yes | | | `host_app` | string | no | | crm_list_contacts [#crm_list_contacts] **promoted read — first-class named tool, capability-gated** · department: `crm` · gate: `read` Filter by tags, lifecycle stage, host app, name; cursor-paginate. | Name | Type | Required | Description | | ----------- | ------- | -------- | ----------- | | `tags_any` | | no | | | `tags_all` | | no | | | `lifecycle` | | no | | | `host_app` | | no | | | `q` | | no | | | `limit` | integer | no | | | `cursor` | | no | | crm_list_stats [#crm_list_stats] **promoted read — first-class named tool, capability-gated** · department: `crm` · gate: `read` Return aggregate counts for the org+host combination: total contacts, active contacts, interactions in the last 30 days, and audience count. *No parameters.* credits_get_balance [#credits_get_balance] **promoted read — first-class named tool, capability-gated** · department: `credits` · gate: `read` Return the owner's credit balance (total, base, top-up, and expiring-soon) | Name | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `owner_id` | string | yes | | | `owner_type` | string | no | Which credit ledger to read/write. Accounts key on (host\_app, owner\_type, owner\_id), so `user` and `org` are two SEPARATE, non-fungible ledgers. Picking the wrong one returns that ledger's real balance (often 0), not an error. | credits_get_ledger [#credits_get_ledger] **promoted read — first-class named tool, capability-gated** · department: `credits` · gate: `read` Owner-scoped credit ledger (transaction history), newest-first, keyset-paginated. | Name | Type | Required | Description | | ------------ | ------- | -------- | ----------- | | `owner_id` | string | yes | | | `owner_type` | string | no | | | `limit` | integer | no | | | `cursor` | | no | | | `kind` | | no | | commerce_get_rate_card [#commerce_get_rate_card] **promoted read — first-class named tool, capability-gated** · department: `commerce` · gate: `read` Return the public per-action rate card for the given host app — live credit costs, the canonical $/credit rate, and editorial pricing categories (404 if no catalog). | Name | Type | Required | Description | | ---------- | ------ | -------- | ----------- | | `host_app` | string | yes | | connects_list_connections [#connects_list_connections] **promoted read — first-class named tool, capability-gated** · department: `connects` · gate: `read` List the tenant's stored provider connections, optionally filtered by provider. | Name | Type | Required | Description | | ---------- | ---- | -------- | ----------- | | `provider` | | no | | connects_list_capabilities [#connects_list_capabilities] **promoted read — first-class named tool, capability-gated** · department: `connects` · gate: `read` Return the registered providers and their available actions and reads, optionally filtered by provider. | Name | Type | Required | Description | | ---------- | ---- | -------- | ----------- | | `provider` | | no | | ads_list_providers [#ads_list_providers] **promoted read — first-class named tool, capability-gated** · department: `adbuyer` · gate: `read` List the ad providers this organization has connected and the reads each supports. *No parameters.* ads_list_campaigns [#ads_list_campaigns] **promoted read — first-class named tool, capability-gated** · department: `adbuyer` · gate: `read` List the org's ad campaigns for `provider` (Meta / Google / TikTok). | Name | Type | Required | Description | | --------------- | ------ | -------- | ---------------------------------------------------------------- | | `provider` | string | yes | Ad provider: meta\_ads \| google\_ads \| tiktok\_ads | | `advertiser_id` | | no | TikTok advertiser id (required for tiktok\_ads campaign listing) | | `status` | | no | Optional campaign status filter (e.g. ACTIVE, PAUSED) | ads_list_insights [#ads_list_insights] **promoted read — first-class named tool, capability-gated** · department: `adbuyer` · gate: `read` Campaign performance metrics for `provider`, optionally narrowed to one campaign and/or a date window. | Name | Type | Required | Description | | --------------- | ------ | -------- | ------------------------------------------------------------- | | `provider` | string | yes | Ad provider: meta\_ads \| google\_ads \| tiktok\_ads | | `campaign_id` | | no | Restrict to a single campaign (where the adapter supports it) | | `advertiser_id` | | no | TikTok advertiser id (single-advertiser insights) | | `level` | | no | Aggregation level, e.g. account \| campaign \| adset \| ad | | `date_preset` | | no | Relative window, e.g. last\_7d, last\_30d, this\_month | | `since` | | no | Start date YYYY-MM-DD (with until, a custom window) | | `until` | | no | End date YYYY-MM-DD (with since, a custom window) | video_list_presets [#video_list_presets] **promoted read — first-class named tool, capability-gated** · department: `studio` · gate: `read` Preset catalog for clip inserts — proxies reeve-remotion `GET /presets` *No parameters.* video_list_projects [#video_list_projects] **promoted read — first-class named tool, capability-gated** · department: `studio` · gate: `read` Org-scoped project list, newest-updated first. | Name | Type | Required | Description | | -------- | ------- | -------- | ---------------------------------------------- | | `limit` | integer | no | Max results returned; silently clamped to 200. | | `offset` | integer | no | Number of results to skip. | video_get_renders [#video_get_renders] **promoted read — first-class named tool, capability-gated** · department: `studio` · gate: `read` Get a reeve-video render's status, scoped to the caller's org. | Name | Type | Required | Description | | ----------- | ------ | -------- | ----------- | | `render_id` | string | yes | | knowledge_list_corpora [#knowledge_list_corpora] **promoted read — first-class named tool, capability-gated** · department: `knowledge` · gate: `read` List the built knowledge corpora with per-corpus fact-card counts. The product lens (host-key) reports only product-visible digest counts as card\_count; product\_label is the customer-facing corpus name. | Name | Type | Required | Description | | ------- | ------ | -------- | -------------------------------------------------------------------------------------------------------- | | `lens` | string | no | product = host-key digests; technical = service-token raw (not reachable over MCP). | | `brand` | | no | Studio brand (required on lens=product) — the corpora list is filtered to the brand's resolved repo set. | knowledge_list_ask [#knowledge_list_ask] **promoted read — first-class named tool, capability-gated** · department: `knowledge` · gate: `read` Semantic Q\&A over grounded fact-cards. lens=product (host-key auth) returns customer-framed prose with source links, scoped to the caller's granted capabilities. | Name | Type | Required | Description | | ------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `q` | string | yes | The question to answer. | | `lens` | string | no | product = host-key, customer-framed digest; developer = host-key (same gate as product), developer-framed API docs; technical = service-token, repo internals (not reachable over MCP). | | `brand` | | no | Studio brand (required on lens=product). The server authorizes the brand against the caller's org and unions the brand's whole repo set. | | `top_k` | integer | no | Maximum number of hits to return. | /mcp/claude — legacy (existing connectors only) [#mcpclaude--legacy-existing-connectors-only] Frozen at exactly these 3 tools for backward compatibility. Don't build new integrations against this mount — connect to `/mcp` instead. chat [#chat-1] Send a message to the user's Reeve agent and get the reply. Args: message: The user's message, verbatim where possible. thread\_id: Conversation id returned by a previous chat call. Omit to start a new conversation. host\_app: Which Reeve product surface to talk to. Default "studio". brand\_id: Optional brand to scope the conversation to (see get\_context). | Name | Type | Required | Description | | ----------- | ------ | -------- | ----------- | | `brand_id` | | no | | | `host_app` | string | no | | | `message` | string | yes | | | `thread_id` | | no | | get_context [#get_context-1] Describe the authenticated Reeve workspace: orgs, user, host\_apps, brands. Brands span ALL the caller's org memberships (DEV-2064) — each brand carries the `org_id` that tracks it plus the `relationship` (`self` = the org's own brand, `competitor` = a tracked competitor). Pass a brand's `id` to `chat` to scope a conversation to it. *No parameters.* resolve_gate [#resolve_gate-1] Approve or cancel a pending gated action from a previous chat reply. Only call after the user explicitly confirmed. decision: "approve"|"cancel". | Name | Type | Required | Description | | ----------- | ------ | -------- | ----------- | | `decision` | string | yes | | | `gate_id` | string | yes | | | `thread_id` | string | yes | | # Quickstart (/docs/developers/api-reference/quickstart) Create a CRM contact with `curl`, then the same call in JavaScript. curl [#curl] ```bash curl -X POST https://api.meetreeve.com/api/crm/v1/contacts \ -H "X-Reeve-Host-Key: rcm_your_key_here" \ -H "X-Reeve-Host-App: your-app" \ -H "Content-Type: application/json" \ -d '{ "contact_type": "person", "display_name": "Ada Lovelace", "identifiers": [{ "kind": "email", "value": "ada@example.com" }] }' ``` JavaScript (fetch) [#javascript-fetch] ```js const res = await fetch("https://api.meetreeve.com/api/crm/v1/contacts", { method: "POST", headers: { "X-Reeve-Host-Key": process.env.REEVE_HOST_KEY, "X-Reeve-Host-App": "your-app", "Content-Type": "application/json", }, body: JSON.stringify({ contact_type: "person", display_name: "Ada Lovelace", identifiers: [{ kind: "email", value: "ada@example.com" }], }), }); const contact = await res.json(); ``` > Official Python and Node SDKs are on the roadmap. Until then, every endpoint works with plain HTTP — use the per-operation reference pages for exact request/response shapes, or the interactive playground on each page. Next: browse the [CRM](/docs/developers/api-reference/crm), [Memory](/docs/developers/api-reference/memory), or [Voice](/docs/developers/api-reference/voice) reference. # Activate Endpoint (/docs/developers/api-reference/booking/activate_endpoint_api_booking_v1_reservations__reservation_id__activate_post) ## POST /api/booking/v1/reservations/{reservation_id}/activate **Activate Endpoint** Transition a reservation to the active (in-use) state and return the updated reservation. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `reservation_id` | path | yes | string | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Availability Endpoint (/docs/developers/api-reference/booking/availability_endpoint_api_booking_v1_resources__resource_id__availability_get) ## GET /api/booking/v1/resources/{resource_id}/availability **Availability Endpoint** Return whether the given resource is currently available to reserve. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `resource_id` | path | yes | string | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Cancel Endpoint (/docs/developers/api-reference/booking/cancel_endpoint_api_booking_v1_reservations__reservation_id__cancel_post) ## POST /api/booking/v1/reservations/{reservation_id}/cancel **Cancel Endpoint** Transition a reservation to the cancelled state and return the updated reservation. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `reservation_id` | path | yes | string | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Confirm Endpoint (/docs/developers/api-reference/booking/confirm_endpoint_api_booking_v1_reservations__reservation_id__confirm_post) ## POST /api/booking/v1/reservations/{reservation_id}/confirm **Confirm Endpoint** Transition a reservation to the confirmed state and return the updated reservation. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `reservation_id` | path | yes | string | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Create Reservation Endpoint (/docs/developers/api-reference/booking/create_reservation_endpoint_api_booking_v1_reservations_post) ## POST /api/booking/v1/reservations **Create Reservation Endpoint** Create (or idempotently return) a reservation against a resource and return it with a created flag. ### Request body Fields: `attrs`, `end_at`, `idempotency_key`, `renter_contact_id`, `resource_id` (required), `start_at` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Create Resource Endpoint (/docs/developers/api-reference/booking/create_resource_endpoint_api_booking_v1_resources_post) ## POST /api/booking/v1/resources **Create Resource Endpoint** Create a bookable resource (e.g. a storage unit, appointment slot, or route) for the caller's host app and return it. ### Request body Fields: `attrs`, `capacity`, `name`, `resource_type` (required), `status` ### Responses | Status | Description | | --- | --- | | 201 | Successful Response | | 422 | Validation Error | # End Endpoint (/docs/developers/api-reference/booking/end_endpoint_api_booking_v1_reservations__reservation_id__end_post) ## POST /api/booking/v1/reservations/{reservation_id}/end **End Endpoint** Transition a reservation to the ended state and return the updated reservation. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `reservation_id` | path | yes | string | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Custom Slack Connect (/docs/developers/api-reference/channels/custom_slack_connect_api_channels_v1_install_custom_connect_post) ## POST /api/channels/v1/install/custom-connect **Custom Slack Connect** Save and validate a user-provided custom Slack bot configuration. For users who created their own Slack app via the manifest. Validates the bot token against the Slack API, then stores credentials as a TeamConnector so the workspace is wired for event routing. If bot_token is not provided, the user still needs to complete the OAuth install — this saves the client credentials for when they do. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Request body Fields: `bot_token`, `client_id` (required), `org_id` (required), `signing_secret` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get Slack Manifest (/docs/developers/api-reference/channels/get_slack_manifest_api_channels_v1_install_manifest_get) ## GET /api/channels/v1/install/manifest **Get Slack Manifest** Return a Slack App Manifest JSON. Users paste this at api.slack.com/apps → Create New App → From App Manifest to create their own Reeve bot with all scopes and URLs pre-configured. After creating the app they copy 2 credentials (Client ID and Signing Secret) and save them via POST /api/channels/v1/install/custom-connect. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `org_id` | query | yes | string | Org ID — caller must be a member | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # List Available Channels (/docs/developers/api-reference/channels/list_available_channels_api_channels_v1_channels__team_connector_id__available_channels_get) ## GET /api/channels/v1/channels/{team_connector_id}/available-channels **List Available Channels** Return Slack channels visible to ``team_connector_id``'s bot. Multi-workspace version of ``install.py::list_slack_channels``. The install variant assumes one Slack connector per org; this one takes the connector id explicitly so the Studio FE can list channels for any of the user's connected workspaces. Errors: 404 — connector doesn't exist. 403 — connector belongs to an org the caller isn't a member of. 400 — connector exists but isn't Slack (no ``conversations.list``). 422 — bot token is missing or corrupted in vault. 502 — Slack API returned ``ok=false``. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `team_connector_id` | path | yes | string | | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # List Slack Channels (/docs/developers/api-reference/channels/list_slack_channels_api_channels_v1_install_channels_get) ## GET /api/channels/v1/install/channels **List Slack Channels** List public Slack channels available to the org's bot (team-scoped). Returns up to 200 channels; truncated=true is set if the workspace has more. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # List Workspaces (/docs/developers/api-reference/channels/list_workspaces_api_channels_v1_channels_get) ## GET /api/channels/v1/channels **List Workspaces** List TeamConnectors visible to the caller, filtered by org membership. ``host_app`` is accepted (FE sends ``"studio"``) but not used for filtering — there's no ``host_app`` column on ``team_connectors`` yet. We log a debug if anything other than ``"studio"`` arrives so we notice. Response shape (the FE in reeve-frontend PR #90 expects this exactly): { "workspaces": [ { "id": ..., "channel_type": "slack", "enabled": true, "createdAt": "...", "config": {"teamId": "...", "teamName": "...", ...}, # token redacted "studio_digest_enabled": false, "studio_digest_channel_id": null, "studio_digest_cron": "0 8 * * *", "studio_digest_brand_id": null } ] } ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `host_app` | query | no | string | | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Save Slack Channels (/docs/developers/api-reference/channels/save_slack_channels_api_channels_v1_install_channels_post) ## POST /api/channels/v1/install/channels **Save Slack Channels** Save the org's selected Slack channels. Admin or owner role required. channel_ids must match Slack's format: C followed by 10 uppercase alphanumerics. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Request body Fields: `channel_ids` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Send Message (/docs/developers/api-reference/channels/send_message_api_channels_v1_send_post) ## POST /api/channels/v1/send **Send Message** Deliver a message to a Slack channel on behalf of a federated caller. Order of operations: 1. **HMAC** — handled by the router-level dependency before we run. 2. **Workspace resolution** — in-memory router cache (same as the inbound webhook path) → ``WorkspaceInfo``. 404 if the workspace isn't connected; the caller can't fix this beyond running install. 3. **Rate limit** — sliding-window per ``workspace_id``. The same limiter fronts inbound dispatch, so a noisy workspace can't exceed its budget by routing through this endpoint. 4. **Token decrypt** — Fernet via :class:`api.services.token_vault.TokenVault`. Failure is a real ops error (bad key / corrupted ciphertext); 500. 5. **Adapter send** — :meth:`SlackAdapter.send` calls ``chat.postMessage``. Any exception from Slack (rate limit, channel not found, token revoked) bubbles as a 502 with the underlying message so the caller can log it. ### Request body Fields: `blocks`, `channel_id` (required), `cost_tag`, `text` (required), `thread_id`, `workspace_id` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Slack Callback (/docs/developers/api-reference/channels/slack_callback_api_channels_v1_install_callback_get) ## GET /api/channels/v1/install/callback **Slack Callback** Handle the OAuth redirect from Slack. Validates the state, exchanges the code for tokens, persists encrypted credentials, creates/updates a TeamConnector, then redirects the user back to the frontend settings page. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `code` | query | no | | | | `state` | query | no | | | | `error` | query | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Slack Install (/docs/developers/api-reference/channels/slack_install_api_channels_v1_install_get) ## GET /api/channels/v1/install **Slack Install** Redirect the authenticated user to Slack's OAuth consent page. Creates an OAuthState record for CSRF protection, then 302-redirects to Slack with the required parameters. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Slack Interactions (/docs/developers/api-reference/channels/slack_interactions_api_channels_v1_interactions_slack_post) ## POST /api/channels/v1/interactions/slack **Slack Interactions** Receive Slack Block Kit button clicks. Slack POSTs ``application/x-www-form-urlencoded`` with a single ``payload`` field whose value is the JSON-encoded interaction. We must respond 200 within 3 seconds. ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | # Update Digest Config (/docs/developers/api-reference/channels/update_digest_config_api_channels_v1_channels__team_connector_id__digest_config_patch) ## PATCH /api/channels/v1/channels/{team_connector_id}/digest-config **Update Digest Config** Update a team_connector's Studio-digest configuration. Order of operations: 1. Load ``TeamConnector`` by id — 404 if missing. 2. Verify the connector's ``team_id`` (org FK) is one of the caller's ``org_ids``. 403 if not. 3. Validate ``channel_id`` (Slack format) and ``cron`` (croniter). Apply each present field to the ORM instance — the SQLAlchemy ``before_update`` hook on ``TeamConnector`` recomputes ``studio_digest_next_run_at`` when cron or enabled changes. 4. If ``brand_id`` was supplied, upsert into ``channel_brand_map`` keyed by ``(team_connector_id, slack_channel_id)``. Effective channel_id is ``patch.channel_id`` if given else the connector's stored value; 400 if neither resolves to a channel. 5. Commit, then build the response — pull the brand mapping for the current channel_id (if any) so the FE shows the live state. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `team_connector_id` | path | yes | string | | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Request body Fields: `brand_id`, `channel_id`, `cron`, `enabled` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Cancel Subscription (/docs/developers/api-reference/commerce/cancel_subscription_api_v2_commerce_subscriptions__subscription_id__cancel_post) ## POST /api/v2/commerce/subscriptions/{subscription_id}/cancel **Cancel Subscription** Cancel at period end — the paid period runs out, no further charges. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `subscription_id` | path | yes | string | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Checkout Session (/docs/developers/api-reference/commerce/checkout_session_api_v2_commerce_checkout_sessions_post) ## POST /api/v2/commerce/checkout-sessions **Checkout Session** Create a one-off Stripe Checkout session on the tenant's Connect account and return its checkout URL and session id. ### Request body Fields: `amount_cents` (required), `cancel_url` (required), `customer_email`, `product_id`, `product_name` (required), `success_url` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Create Subscription (/docs/developers/api-reference/commerce/create_subscription_api_v2_commerce_subscriptions_post) ## POST /api/v2/commerce/subscriptions **Create Subscription** Recurring checkout on the tenant's Connect account (DEV-3644) — tuition, dues, memberships. Same 1% take-rate as one-off checkout. ### Request body Fields: `amount_cents` (required), `cancel_url` (required), `customer_email`, `interval`, `product_id`, `product_name` (required), `success_url` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get Commerce Catalog (/docs/developers/api-reference/commerce/get_commerce_catalog_api_v2_commerce_catalog__host_app__get) ## GET /api/v2/commerce/catalog/{host_app} **Get Commerce Catalog** Return the public plan catalog for the given host app (404 if no catalog is registered). ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `host_app` | path | yes | string | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get Commerce Rate Card (/docs/developers/api-reference/commerce/get_commerce_rate_card_api_v2_commerce_rate_card__host_app__get) ## GET /api/v2/commerce/rate-card/{host_app} **Get Commerce Rate Card** Return the public per-action rate card for the given host app — live credit costs, the canonical $/credit rate, and editorial pricing categories (404 if no catalog). ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `host_app` | path | yes | string | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Onboard (/docs/developers/api-reference/commerce/onboard_api_v2_commerce_tenant_onboard_post) ## POST /api/v2/commerce/tenant/onboard **Onboard** Get or create the tenant's Stripe Connect account and return its account id plus a hosted onboarding URL. ### Request body Fields: `email` (required), `product_id`, `refresh_url`, `return_url` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Onboard Status (/docs/developers/api-reference/commerce/onboard_status_api_v2_commerce_tenant_onboard_status_get) ## GET /api/v2/commerce/tenant/onboard/status **Onboard Status** Return the tenant's Stripe Connect onboarding status (charges/payouts enabled, details submitted); 404 if no account exists. ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | # Refund (/docs/developers/api-reference/commerce/refund_api_v2_commerce_refunds_post) ## POST /api/v2/commerce/refunds **Refund** Refund a payment intent belonging to the tenant's org, fully or partially; 404 if it does not belong to the caller. ### Request body Fields: `amount_cents`, `payment_intent_id` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Revenue (/docs/developers/api-reference/commerce/revenue_api_v2_commerce_tenant_revenue_get) ## GET /api/v2/commerce/tenant/revenue **Revenue** Return the tenant's revenue summary for their Stripe Connect account. ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | # Subscriptions (/docs/developers/api-reference/commerce/subscriptions_api_v2_commerce_subscriptions_get) ## GET /api/v2/commerce/subscriptions **Subscriptions** The tenant's recurring charges; status == past_due means Stripe Smart Retries are dunning a failed charge (DEV-3644). ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | # Create Enrollment (/docs/developers/api-reference/comms/create_enrollment_v1_comms_flows__flow_id__enrollments_post) ## POST /v1/comms/flows/{flow_id}/enrollments **Create Enrollment** Enrol a recipient into a comms flow. Idempotency: supply X-Idempotency-Key to make the call safe to retry. Two requests with the same key from the same host_app within the TTL window return the same enrollment_id without creating a second row. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `flow_id` | path | yes | string | | | `X-Comms-Key` | header | no | | | | `X-Reeve-Host-Key` | header | no | | | ### Request body Fields: `channel` (required), `context`, `email`, `phone`, `recipient_id` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Emit Event (/docs/developers/api-reference/comms/emit_event_v1_comms_events_post) ## POST /v1/comms/events **Emit Event** Emit a semantic event. Supply X-Idempotency-Key to make a re-emit safe; without one, (recipient_id, event_name, context.cycle) is the idempotency discriminator. Returns 204 (no body) when the consumer has no enabled binding for the event. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Comms-Key` | header | no | | | | `X-Reeve-Host-Key` | header | no | | | ### Request body Fields: `channel`, `context`, `email`, `event_name` (required), `phone`, `recipient_id` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # List Push Tokens (/docs/developers/api-reference/comms/list_push_tokens_v1_comms_push_tokens_get) ## GET /v1/comms/push/tokens **List Push Tokens** List tokens for a subscriber within this host_app. Default hides revoked rows; pass include_revoked=true for admin/debug visibility. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `subscriber_id` | query | yes | string | | | `include_revoked` | query | no | boolean | | | `X-Comms-Key` | header | no | | | | `X-Reeve-Host-Key` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Register Push Token (/docs/developers/api-reference/comms/register_push_token_v1_comms_push_tokens_post) ## POST /v1/comms/push/tokens **Register Push Token** UPSERT a device token. Returning the same (host_app, channel, token) twice is idempotent — touches last_seen_at, clears revoked_at. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Comms-Key` | header | no | | | | `X-Reeve-Host-Key` | header | no | | | ### Request body Fields: `channel` (required), `platform`, `subscriber_id` (required), `token` (required), `user_agent` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Revoke Push Token (/docs/developers/api-reference/comms/revoke_push_token_v1_comms_push_tokens__token_id__delete) ## DELETE /v1/comms/push/tokens/{token_id} **Revoke Push Token** Soft-delete (sets revoked_at). Cross-host_app 404 — never reveal existence of another host_app's token id. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `token_id` | path | yes | string | | | `X-Comms-Key` | header | no | | | | `X-Reeve-Host-Key` | header | no | | | ### Responses | Status | Description | | --- | --- | | 204 | Successful Response | | 422 | Validation Error | # Send By Template (/docs/developers/api-reference/comms/send_by_template_v1_comms_send_by_template_post) ## POST /v1/comms/send-by-template **Send By Template** Resolve a stored comms_templates row, render with variables, dispatch. Channel is implied by the resolved template row, not the request body. See DEV-866 spec on Linear for the design context. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Idempotency-Key` | header | no | | | | `X-Comms-Key` | header | no | | | | `X-Reeve-Host-Key` | header | no | | | ### Request body Fields: `bcc`, `cc`, `headers`, `principal_id`, `provider`, `reply_to`, `stream`, `subscriber_id`, `tags`, `template_name` (required), `to` (required), `variables` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Send Rendered (/docs/developers/api-reference/comms/send_rendered_v1_comms_send_rendered_post) ## POST /v1/comms/send-rendered **Send Rendered** Send a pre-rendered email via Resend, log to comms_events. Required: `to_email`, `subject`, and one of `html` / `text`. Optional: `headers` (List-Unsubscribe etc.), `reply_to`, `cc`/`bcc`, `tags`, `template_id`. Authenticates via X-Comms-Key (same scheme as /v1/comms/send). The `host_app.from_email` is the sender — Phase 1 has no per-call from override because the per-app branding rule (Freya ↔ meetfreya.com, etc.) is enforced at the host_app level. DEV-839 Phase 3 (CR fix 2): the idempotency path is reserve-key-first. The route INSERTs a 'pending' row before the Resend POST so a concurrent duplicate cannot also fire the upstream call — the loser waits for the winner's status to flip to 'sent' (or 'failed') and returns the winner's send_id. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Idempotency-Key` | header | no | | | | `X-Comms-Key` | header | no | | | | `X-Reeve-Host-Key` | header | no | | | ### Request body Fields: `bcc`, `cc`, `headers`, `html`, `principal_id`, `provider`, `reply_to`, `stream`, `subject` (required), `tags`, `template_id`, `text`, `to_email` (required), `to_name` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Send (/docs/developers/api-reference/comms/send_v1_comms_send_post) ## POST /v1/comms/send **Send** Send a message (email or SMS) through Reeve.Comms to a recipient. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Idempotency-Key` | header | no | | | | `X-Comms-Key` | header | no | | | | `X-Reeve-Host-Key` | header | no | | | ### Request body Fields: `payload`, `principal_id`, `stream`, `to` (required), `transaction_id`, `workflow` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Create Webhook Subscription (/docs/developers/api-reference/connects/create_webhook_subscription_api_connects_v1_webhook_subscriptions_post) ## POST /api/connects/v1/webhook_subscriptions **Create Webhook Subscription** Create an outbound webhook subscription for a provider event and return its id plus a one-time signing secret. ### Request body Fields: object ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Delete Connection (/docs/developers/api-reference/connects/delete_connection_api_connects_v1_connections__conn_id__delete) ## DELETE /api/connects/v1/connections/{conn_id} **Delete Connection** Delete the tenant's stored provider connection by id (204 on success). ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `conn_id` | path | yes | string | | ### Responses | Status | Description | | --- | --- | | 204 | Successful Response | | 422 | Validation Error | # Delete Webhook Subscription (/docs/developers/api-reference/connects/delete_webhook_subscription_api_connects_v1_webhook_subscriptions__sub_id__delete) ## DELETE /api/connects/v1/webhook_subscriptions/{sub_id} **Delete Webhook Subscription** Deactivate the tenant's webhook subscription by id (204 on success). ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `sub_id` | path | yes | string | | ### Responses | Status | Description | | --- | --- | | 204 | Successful Response | | 422 | Validation Error | # Execute Action (/docs/developers/api-reference/connects/execute_action_api_connects_v1_execute__provider___action__post) ## POST /api/connects/v1/execute/{provider}/{action} **Execute Action** Execute a provider action (or read) with the given args against the tenant's stored connection and return the adapter result. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `provider` | path | yes | string | | | `action` | path | yes | string | | ### Request body Fields: object ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get Capabilities (/docs/developers/api-reference/connects/get_capabilities_api_connects_v1_capabilities_get) ## GET /api/connects/v1/capabilities **Get Capabilities** Return the registered providers and their available actions and reads, optionally filtered by provider. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `provider` | query | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # List Connections (/docs/developers/api-reference/connects/list_connections_api_connects_v1_connections_get) ## GET /api/connects/v1/connections **List Connections** List the tenant's stored provider connections, optionally filtered by provider. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `provider` | query | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Meta Ensure Brand Pixel (/docs/developers/api-reference/connects/meta_ensure_brand_pixel_api_connects_v1_meta_brand_accounts__brand_id__pixel_post) ## POST /api/connects/v1/meta/brand-accounts/{brand_id}/pixel **Meta Ensure Brand Pixel** Ensure the brand has a Meta Pixel (DEV-2616): reuse the saved one, adopt the ad account's existing pixel, or create one — then persist it into the brand_accounts map PublisherDirector reads. Body (optional): {name?, ad_account_id?}. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `brand_id` | path | yes | string | | ### Request body (see schema) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Meta Get Brand Account (/docs/developers/api-reference/connects/meta_get_brand_account_api_connects_v1_meta_brand_accounts__brand_id__get) ## GET /api/connects/v1/meta/brand-accounts/{brand_id} **Meta Get Brand Account** Return the saved Meta ad-account/Page/Pixel selection stored for the given brand. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `brand_id` | path | yes | string | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Meta List Ad Accounts (/docs/developers/api-reference/connects/meta_list_ad_accounts_api_connects_v1_meta_ad_accounts_get) ## GET /api/connects/v1/meta/ad-accounts **Meta List Ad Accounts** List the Meta ad accounts reachable with the tenant's connected Meta credential. ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | # Meta List Pages (/docs/developers/api-reference/connects/meta_list_pages_api_connects_v1_meta_pages_get) ## GET /api/connects/v1/meta/pages **Meta List Pages** List the Facebook Pages reachable with the tenant's connected Meta credential. ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | # Meta List Pixels (/docs/developers/api-reference/connects/meta_list_pixels_api_connects_v1_meta_pixels_get) ## GET /api/connects/v1/meta/pixels **Meta List Pixels** List the Meta Pixels on the given ad account (the ad_account_id query param is required). ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `ad_account_id` | query | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Meta Put Brand Account (/docs/developers/api-reference/connects/meta_put_brand_account_api_connects_v1_meta_brand_accounts__brand_id__put) ## PUT /api/connects/v1/meta/brand-accounts/{brand_id} **Meta Put Brand Account** Persist the Meta ad-account/Page/Pixel selection for the given brand and return the stored mapping. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `brand_id` | path | yes | string | | ### Request body Fields: object ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Oauth Callback (/docs/developers/api-reference/connects/oauth_callback_api_connects_v1_connect__provider__callback_get) ## GET /api/connects/v1/connect/{provider}/callback **Oauth Callback** Exchange the code for credentials, persist the connection, redirect. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `provider` | path | yes | string | | | `code` | query | yes | string | | | `state` | query | yes | string | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Oauth Start (/docs/developers/api-reference/connects/oauth_start_api_connects_v1_connect__provider__start_post) ## POST /api/connects/v1/connect/{provider}/start **Oauth Start** Return an authorize_url the caller should redirect the user to. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `provider` | path | yes | string | | ### Request body Fields: object ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Debit (/docs/developers/api-reference/credits/debit_api_v1_credits_debit_post) ## POST /api/v1/credits/debit **Debit** Debit a fixed credit amount from an owner and return the debited amount and ledger entry id, or 402 if the balance is insufficient. ### Request body Fields: `amount` (required), `owner_id` (required), `owner_type`, `reason` (required), `reference_id` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Debit Usage (/docs/developers/api-reference/credits/debit_usage_api_v1_credits_debit_usage_post) ## POST /api/v1/credits/debit/usage **Debit Usage** Debit credits computed from LLM token usage (model plus input/output/cache tokens at the host app's markup) and return the debited amount and ledger entry id, or 402 if insufficient. ### Request body Fields: `cache_read_tokens`, `input_tokens` (required), `model` (required), `output_tokens` (required), `owner_id` (required), `owner_type`, `reason` (required), `reference_id` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get Balance (/docs/developers/api-reference/credits/get_balance_api_v1_credits_balance__owner_id__get) ## GET /api/v1/credits/balance/{owner_id} **Get Balance** Return the owner's credit balance (total, base, top-up, and expiring-soon amounts) for the caller's host app. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `owner_id` | path | yes | string | | | `owner_type` | query | no | string | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get View (/docs/developers/api-reference/credits/get_view_api_v1_credits_view__owner_id__get) ## GET /api/v1/credits/view/{owner_id} **Get View** Host-app display view — Freya's percentage meter (``consumed/granted``). ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `owner_id` | path | yes | string | | | `owner_type` | query | no | string | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Grant (/docs/developers/api-reference/credits/grant_api_v1_credits_grant_post) ## POST /api/v1/credits/grant **Grant** Grant credits to an owner and return the granted amount and the ledger entry id. ### Request body Fields: `amount` (required), `expires_at`, `owner_id` (required), `owner_type`, `reference_id` (required), `source` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Refund (/docs/developers/api-reference/credits/refund_api_v1_credits_refund_post) ## POST /api/v1/credits/refund **Refund** Refund the credits previously debited under the given reference id and report whether a refund was applied. ### Request body Fields: `owner_id` (required), `owner_type`, `reason`, `reference_id` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Connection (/docs/developers/api-reference/database/connection_api_database_v1_connection_get) ## GET /api/database/v1/connection **Connection** Return the connection DSN and schema name for the caller's provisioned database (call POST /provision first). ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | # Instances (/docs/developers/api-reference/database/instances_api_database_v1_instances_get) ## GET /api/database/v1/instances **Instances** List the database instances available to the caller's host app, with each instance's slug, name, default schema, and allowed schemas. ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | # Provision (/docs/developers/api-reference/database/provision_api_database_v1_provision_post) ## POST /api/database/v1/provision **Provision** Provision a dedicated managed-cluster schema for the caller's host app and return its schema name and status. ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | # Query (/docs/developers/api-reference/database/query_api_database_v1_query_post) ## POST /api/database/v1/query **Query** Run a read-only, parameterized SELECT against the caller's database instance and return the resulting rows. ### Request body Fields: `instance` (required), `max_rows`, `params`, `sql` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Read Template Run (/docs/developers/api-reference/database/read_template_run_api_database_v1_read_templates__name__run_post) ## POST /api/database/v1/read/templates/{name}/run **Read Template Run** Execute a named, pre-registered read template over the provisioned database in a read-only transaction and return the resulting rows. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `name` | path | yes | string | | ### Request body Fields: `params` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Run Template (/docs/developers/api-reference/database/run_template_api_database_v1_templates__name__run_post) ## POST /api/database/v1/templates/{name}/run **Run Template** Execute a named, pre-registered SQL template against the bound instance with the given params and return the resulting rows. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `name` | path | yes | string | | ### Request body Fields: `instance` (required), `params` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Schema (/docs/developers/api-reference/database/schema_api_database_v1_schema_get) ## GET /api/database/v1/schema **Schema** Introspect and return the table and column schema of the named database instance. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `instance` | query | yes | string | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Write Template Run (/docs/developers/api-reference/database/write_template_run_api_database_v1_write_templates__name__run_post) ## POST /api/database/v1/write/templates/{name}/run **Write Template Run** Execute a named, pre-registered write (DML) template on the provisioned writer database and return the affected-row result. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `name` | path | yes | string | | ### Request body Fields: `params` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Anti Claims (/docs/developers/api-reference/dna/anti_claims_api_brand_profiler_anti_claims_post) ## POST /api/brand-profiler/anti-claims **Anti Claims** Derive 5–10 single-word descriptors a brand explicitly disavows (Supply: ['disposable', 'plastic', 'single-use', ...]). Downstream agents — image classifier, caption writer, strategist — consult this list to reject brand-incorrect output. Killed the "Supply razor → 'disposable safety razor'" hallucination during eval batch 2026-05-02. ### Request body Fields: `brand_name` (required), `brand_one_liner` (required), `homepage_copy`, `verified_facts` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Image Search (/docs/developers/api-reference/dna/image_search_api_brand_profiler_image_search_post) ## POST /api/brand-profiler/image-search **Image Search** Multi-provider brand image search. Chains Tavily → Brave → Google CSE, returning the first provider's results that satisfy `min_results`. Useful when a downstream product (caption writer, ad generator) only needs image URLs and doesn't want to trigger the full DNA scrape. ### Request body Fields: `brand_name` (required), `min_results`, `per_query`, `providers`, `queries` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Palette (/docs/developers/api-reference/dna/palette_api_brand_profiler_palette_post) ## POST /api/brand-profiler/palette **Palette** Vision-based brand palette extraction. Two-pass: k-means quantization on the pixel buffers + Claude Sonnet visual analysis, merged with ΔE-30 reconciliation. For brands whose homepage CSS doesn't reflect the actual brand palette (Kiehl's, Aesop, Le Labo — the wordmark color lives in packaging, not the stylesheet), this endpoint returns the actual visual identity. ### Request body Fields: `brand_name` (required), `image_urls` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Accept all suggestions for a job (/docs/developers/api-reference/crm/accept_all_for_job_api_crm_v1_jobs__job_id__accept_all_post) ## POST /api/crm/v1/jobs/{job_id}/accept_all **Accept all suggestions for a job** Bulk-apply every pending suggestion. Returns per-suggestion accept/fail lists. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `job_id` | path | yes | string | | | `X-Org-Id` | header | no | | | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Accept enrichment suggestion (/docs/developers/api-reference/crm/accept_suggestion_api_crm_v1_suggestions__suggestion_id__accept_post) ## POST /api/crm/v1/suggestions/{suggestion_id}/accept **Accept enrichment suggestion** Apply a pending suggestion via the target handler (contact-targeted suggestions write to crmhub_contacts.extensions). ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `suggestion_id` | path | yes | string | | | `X-Org-Id` | header | no | | | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Add an identifier to a contact. (/docs/developers/api-reference/crm/add_identifier_endpoint_api_crm_v1_contacts__contact_id__identifiers_post) ## POST /api/crm/v1/contacts/{contact_id}/identifiers **Add an identifier to a contact.** Attach an additional identifier (email, phone, external_id, etc.) to an existing contact. Returns 200 if the identifier already exists on the same contact; 409 if it belongs to a different contact. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `contact_id` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Request body Fields: `is_primary`, `kind` (required), `value` (required), `verified` ### Responses | Status | Description | | --- | --- | | 201 | Successful Response | | 422 | Validation Error | # Add a note to a contact. (/docs/developers/api-reference/crm/add_note_endpoint_api_crm_v1_contacts__contact_id__notes_post) ## POST /api/crm/v1/contacts/{contact_id}/notes **Add a note to a contact.** Attach a free-text note to a contact, attributed to the calling user. Notes are ordered by creation time in the contact's timeline. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `contact_id` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Request body Fields: `body` (required) ### Responses | Status | Description | | --- | --- | | 201 | Successful Response | | 422 | Validation Error | # LLM-propose a fresh CRM config from the tenant's brand DNA. (/docs/developers/api-reference/crm/bootstrap_config_endpoint_api_crm_v1_config_bootstrap_post) ## POST /api/crm/v1/config/bootstrap **LLM-propose a fresh CRM config from the tenant's brand DNA.** Reads the per-tenant brand pack and asks Claude to propose a validated crm_config document (3-6 entity_types, 4-8 views). Does NOT persist — the caller previews and may then PUT it. Returns 502 with code='bootstrap_failed' if the LLM produced invalid output across all retries. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Bulk-ingest interactions (max 5k per request). (/docs/developers/api-reference/crm/bulk_log_interactions_endpoint_api_crm_v1_interactions_bulk_post) ## POST /api/crm/v1/interactions/bulk **Bulk-ingest interactions (max 5k per request).** Sync ingest up to 5k items. Contacts resolved by id or identifier. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Request body Fields: `interactions` (required), `lookup_strategy` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Bulk upsert contacts. Sync ≤1k items; async >1k items. (/docs/developers/api-reference/crm/bulk_upsert_contacts_endpoint_api_crm_v1_contacts_bulk_post) ## POST /api/crm/v1/contacts/bulk **Bulk upsert contacts. Sync ≤1k items; async >1k items.** Upsert a list of contacts in a single request. Batches of 1k or fewer are processed synchronously (200). Larger batches are enqueued as a background job and return 202 with a `job_id` to poll via GET /jobs/{job_id}. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Request body Fields: array ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Create an audience from a filter spec. (/docs/developers/api-reference/crm/create_audience_endpoint_api_crm_v1_audiences_post) ## POST /api/crm/v1/audiences **Create an audience from a filter spec.** Evaluate a filter expression against the org's contacts and persist the matching set as a named audience. The `count` in the response reflects the member count at creation time; membership is not updated automatically. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Request body Fields: `filter` (required), `name` ### Responses | Status | Description | | --- | --- | | 201 | Successful Response | | 422 | Validation Error | # Create a relationship between two contacts. (/docs/developers/api-reference/crm/create_relationship_endpoint_api_crm_v1_relationships_post) ## POST /api/crm/v1/relationships **Create a relationship between two contacts.** Establish a directed, typed relationship between two contacts in the same org. Returns 409 if an identical (from, to, kind) relationship already exists. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Request body Fields: `from_contact_id` (required), `kind` (required), `metadata`, `to_contact_id` (required) ### Responses | Status | Description | | --- | --- | | 201 | Successful Response | | 422 | Validation Error | # Crm Chat Action (/docs/developers/api-reference/crm/crm_chat_action_api_crm_v1_chat_action__thread_id__post) ## POST /api/crm/v1/chat/action/{thread_id} **Crm Chat Action** Approve/cancel a pending ChatActionQueue row from the CRM panel. DEV-2221. Mirrors `studio_chat.action` but scopes via the conversation's (org_id, host_app) binding rather than studio's `_claim_or_verify_thread`. Approve commits BEFORE dispatch (so the cross-session dispatcher sees the durable 'approved' status); cancel emits a `declined` outcome. Raced / terminal rows -> 409 (the InvalidTransitionError contract from approval_queue). ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `thread_id` | path | yes | string | | ### Request body Fields: `decision` (required), `gate_id` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 404 | Gate not found, or conversation ownership mismatch (cross-org / cross-host / cross-thread). Foreign gate ids collapse to 404 so they're indistinguishable from missing. | | 409 | Queue row is no longer pending (already approved / discarded / failed) — `approval_queue.InvalidTransitionError`. | | 422 | Validation Error | # Crm Web Chat (/docs/developers/api-reference/crm/crm_web_chat_api_crm_v1_chat_post) ## POST /api/crm/v1/chat **Crm Web Chat** Run one CRM admin chat turn through the reeve persona in CRM mode. Returns ``{conversation_id, reply, tool_calls, mode}``. The org/host scope rides the conversation metadata so the CRM ToolSpec handler can enforce it. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | ### Request body Fields: object ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Soft-delete a contact. (/docs/developers/api-reference/crm/delete_contact_endpoint_api_crm_v1_contacts__contact_id__delete) ## DELETE /api/crm/v1/contacts/{contact_id} **Soft-delete a contact.** Marks a contact as deleted without removing the record. Soft-deleted contacts are excluded from list and get operations. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `contact_id` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 204 | Successful Response | | 422 | Validation Error | # Delete a contact's extension by type. (/docs/developers/api-reference/crm/delete_extension_endpoint_api_crm_v1_contacts__contact_id__extensions__extension_type__delete) ## DELETE /api/crm/v1/contacts/{contact_id}/extensions/{extension_type} **Delete a contact's extension by type.** Remove the stored extension blob for the given contact and type. Returns 404 if the extension or contact does not exist. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `contact_id` | path | yes | string | | | `extension_type` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 204 | Successful Response | | 422 | Validation Error | # Delete an identifier. Forbidden if it would leave the contact with zero identifiers. (/docs/developers/api-reference/crm/delete_identifier_endpoint_api_crm_v1_identifiers__identifier_id__delete) ## DELETE /api/crm/v1/identifiers/{identifier_id} **Delete an identifier. Forbidden if it would leave the contact with zero identifiers.** Remove an identifier from a contact. Returns 409 if this is the contact's last identifier — every contact must retain at least one. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `identifier_id` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 204 | Successful Response | | 422 | Validation Error | # Delete a note (author or admin only). (/docs/developers/api-reference/crm/delete_note_endpoint_api_crm_v1_notes__note_id__delete) ## DELETE /api/crm/v1/notes/{note_id} **Delete a note (author or admin only).** Permanently delete a note. Only the original author or a user with the `crm:admin` scope may delete a note. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `note_id` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 204 | Successful Response | | 422 | Validation Error | # Delete a relationship. (/docs/developers/api-reference/crm/delete_relationship_endpoint_api_crm_v1_relationships__relationship_id__delete) ## DELETE /api/crm/v1/relationships/{relationship_id} **Delete a relationship.** Permanently remove a directed relationship by its ID. Returns 404 if the relationship does not exist or belongs to another org. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `relationship_id` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 204 | Successful Response | | 422 | Validation Error | # Dismiss enrichment suggestion (/docs/developers/api-reference/crm/dismiss_suggestion_endpoint_api_crm_v1_suggestions__suggestion_id__dismiss_post) ## POST /api/crm/v1/suggestions/{suggestion_id}/dismiss **Dismiss enrichment suggestion** Mark a pending suggestion as rejected; no writeback fires. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `suggestion_id` | path | yes | string | | | `X-Org-Id` | header | no | | | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Enqueue contact enrichment (/docs/developers/api-reference/crm/enqueue_enrichment_api_crm_v1_contacts__contact_id__enrich_post) ## POST /api/crm/v1/contacts/{contact_id}/enrich **Enqueue contact enrichment** Schedule a recipe-driven enrichment job for the contact. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `contact_id` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | | `Idempotency-Key` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Request body Fields: `budget_usd`, `recipe_override` ### Responses | Status | Description | | --- | --- | | 202 | Successful Response | | 400 | Bad Request | | 404 | Not Found | | 409 | Conflict | | 422 | Validation Error | # Enroll an audience's members into a Comms flow. (/docs/developers/api-reference/crm/enroll_audience_in_flow_endpoint_api_crm_v1_audiences__audience_id__enroll_in_flow_post) ## POST /api/crm/v1/audiences/{audience_id}/enroll-in-flow **Enroll an audience's members into a Comms flow.** Bulk-enroll every member of a saved audience into a Comms flow (a drip/sequence). Members without an email are skipped; idempotent per (flow, contact) — a contact already in the flow is skipped, even across overlapping audiences. Returns per-outcome counts. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `audience_id` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Request body Fields: `channel`, `flow_id` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get enrichment artifact presigned URL (/docs/developers/api-reference/crm/get_artifact_url_api_crm_v1_enrichment_artifacts__artifact_id__get) ## GET /api/crm/v1/enrichment/artifacts/{artifact_id} **Get enrichment artifact presigned URL** Return a 5-minute presigned S3 URL for the artifact (e.g. a re-hosted scraped photo). ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `artifact_id` | path | yes | string | | | `X-Org-Id` | header | no | | | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get an audience by id. (/docs/developers/api-reference/crm/get_audience_endpoint_api_crm_v1_audiences__audience_id__get) ## GET /api/crm/v1/audiences/{audience_id} **Get an audience by id.** Return the metadata and member count for a previously created audience. Returns 404 if the audience does not exist or belongs to another org. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `audience_id` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Per-tenant branding for the current org+host. (/docs/developers/api-reference/crm/get_branding_me_endpoint_api_crm_v1_branding_me_get) ## GET /api/crm/v1/branding/me **Per-tenant branding for the current org+host.** Returns the deep-merged brand pack (profile_json + overrides_json) for the calling org/host. If no row exists yet, returns neutral defaults and asynchronously kicks off a brand-profiler bootstrap. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get the active CRM config for the calling org+host. (/docs/developers/api-reference/crm/get_config_me_endpoint_api_crm_v1_config_me_get) ## GET /api/crm/v1/config/me **Get the active CRM config for the calling org+host.** Returns the per-tenant crm_config document that drives the schema-driven frontend. 404 with code='config_not_found' if the tenant has not run bootstrap or PUT yet. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Prior versions of the active CRM config. (/docs/developers/api-reference/crm/get_config_me_history_endpoint_api_crm_v1_config_me_history_get) ## GET /api/crm/v1/config/me/history **Prior versions of the active CRM config.** Returns up to `limit` prior versions, newest-first. 404 with code='config_not_found' if the tenant has no active config yet. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `limit` | query | no | integer | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get a contact by ID (/docs/developers/api-reference/crm/get_contact_endpoint_api_crm_v1_contacts__contact_id__get) ## GET /api/crm/v1/contacts/{contact_id} **Get a contact by ID** Returns full detail including identifiers, extensions, and relationships (both directions). 404 for missing, soft-deleted, or cross-org (no existence leak). ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `contact_id` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get all relationships of a contact, split into outgoing and incoming. (/docs/developers/api-reference/crm/get_contact_relationships_endpoint_api_crm_v1_contacts__contact_id__relationships_get) ## GET /api/crm/v1/contacts/{contact_id}/relationships **Get all relationships of a contact, split into outgoing and incoming.** Returns two lists: `outgoing` (relationships where this contact is the source) and `incoming` (relationships where this contact is the target). ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `contact_id` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get enrichment job (/docs/developers/api-reference/crm/get_enrichment_job_api_crm_v1_enrichment_jobs__job_id__get) ## GET /api/crm/v1/enrichment/jobs/{job_id} **Get enrichment job** Fetch a single enrichment job's status, telemetry, and cost. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `job_id` | path | yes | string | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get a contact's extension by type. (/docs/developers/api-reference/crm/get_extension_endpoint_api_crm_v1_contacts__contact_id__extensions__extension_type__get) ## GET /api/crm/v1/contacts/{contact_id}/extensions/{extension_type} **Get a contact's extension by type.** Retrieve the stored extension blob for the given contact and type. Returns 404 if no extension of that type exists for this contact. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `contact_id` | path | yes | string | | | `extension_type` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get a contact's interaction timeline. (/docs/developers/api-reference/crm/get_interaction_timeline_endpoint_api_crm_v1_contacts__contact_id__interactions_get) ## GET /api/crm/v1/contacts/{contact_id}/interactions **Get a contact's interaction timeline.** Newest-first cursor pagination. Filter by channel, direction, since. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `contact_id` | path | yes | string | | | `channel` | query | no | | | | `direction` | query | no | | | | `since` | query | no | | | | `limit` | query | no | integer | | | `cursor` | query | no | | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Job status (bulk upsert, etc.). (/docs/developers/api-reference/crm/get_job_endpoint_api_crm_v1_jobs__job_id__get) ## GET /api/crm/v1/jobs/{job_id} **Job status (bulk upsert, etc.).** Poll the status of an asynchronous background job. Terminal statuses are `completed` and `failed`; intermediate status is `running`. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `job_id` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Org-scoped CRM stats for the calling host. (/docs/developers/api-reference/crm/get_stats_api_crm_v1_stats_get) ## GET /api/crm/v1/stats **Org-scoped CRM stats for the calling host.** Return aggregate counts for the org+host combination: total contacts, active contacts, interactions in the last 30 days, and audience count. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Handle Tool Call (/docs/developers/api-reference/crm/handle_tool_call_api_crm_v1_chat_tools_post) ## POST /api/crm/v1/chat-tools **Handle Tool Call** Execute one CRM persona tool call. Returns the Reeve.Chat handler contract: ``{ok: true, result: ...}`` / ``{ok: false, error: {...}}`` — tool-level failures are 200s with ok=false (conversational facts); only transport failures (bad signature, malformed envelope, unknown conversation) are HTTP errors. ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | # Paginated audience members. (/docs/developers/api-reference/crm/list_audience_members_endpoint_api_crm_v1_audiences__audience_id__members_get) ## GET /api/crm/v1/audiences/{audience_id}/members **Paginated audience members.** Return the contacts belonging to an audience in creation-order cursor pages. Use the `next_cursor` from each response to fetch subsequent pages. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `audience_id` | path | yes | string | | | `limit` | query | no | integer | | | `cursor` | query | no | | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # List contact's enrichment jobs (/docs/developers/api-reference/crm/list_contact_jobs_api_crm_v1_contacts__contact_id__jobs_get) ## GET /api/crm/v1/contacts/{contact_id}/jobs **List contact's enrichment jobs** List the most recent enrichment jobs run against a contact. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `contact_id` | path | yes | string | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # List contacts (/docs/developers/api-reference/crm/list_contacts_endpoint_api_crm_v1_contacts_get) ## GET /api/crm/v1/contacts **List contacts** Filter by tags, lifecycle stage, host app, name; cursor-paginate. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `tags_any` | query | no | | | | `tags_all` | query | no | | | | `lifecycle` | query | no | | | | `host_app` | query | no | | | | `q` | query | no | | | | `limit` | query | no | integer | | | `cursor` | query | no | | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # List contact's enrichment suggestions (/docs/developers/api-reference/crm/list_suggestions_api_crm_v1_contacts__contact_id__suggestions_get) ## GET /api/crm/v1/contacts/{contact_id}/suggestions **List contact's enrichment suggestions** List enrichment suggestions for a contact. Filter by `status`. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `contact_id` | path | yes | string | | | `status` | query | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Log a single interaction. (/docs/developers/api-reference/crm/log_interaction_endpoint_api_crm_v1_interactions_post) ## POST /api/crm/v1/interactions **Log a single interaction.** Idempotent on (host_app_id, external_id). Returns 200 with the existing record on duplicate. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Request body Fields: `campaign_id`, `channel` (required), `contact_id` (required), `content_asset_id`, `currency`, `direction` (required), `external_id`, `occurred_at` (required), `payload`, `summary`, `value` ### Responses | Status | Description | | --- | --- | | 201 | Successful Response | | 422 | Validation Error | # Merge source contact into target. Source is soft-deleted. (/docs/developers/api-reference/crm/merge_contact_endpoint_api_crm_v1_contacts__contact_id__merge_post) ## POST /api/crm/v1/contacts/{contact_id}/merge **Merge source contact into target. Source is soft-deleted.** Merges the identified contact (source) into another contact (target). Identifiers, interactions, notes and extensions are re-parented; the source is soft-deleted after a successful merge. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `contact_id` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Request body Fields: `into` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Merge a partial payload into a contact's extension (atomic). (/docs/developers/api-reference/crm/merge_extension_endpoint_api_crm_v1_contacts__contact_id__extensions__extension_type__patch) ## PATCH /api/crm/v1/contacts/{contact_id}/extensions/{extension_type} **Merge a partial payload into a contact's extension (atomic).** Partial update: merges the given payload keys into the existing extension under a row lock, so concurrent field updates don't clobber each other (unlike a client-side GET-modify-PUT). Keys in append_keys are string-appended instead of replaced. Creates the extension if absent. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `contact_id` | path | yes | string | | | `extension_type` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Request body Fields: `append_keys`, `payload` (required), `schema_version` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Partial update of a contact. (/docs/developers/api-reference/crm/patch_contact_endpoint_api_crm_v1_contacts__contact_id__patch) ## PATCH /api/crm/v1/contacts/{contact_id} **Partial update of a contact.** Apply a partial update to an existing contact's fields. Only supplied fields are modified; omitted fields are left unchanged. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `contact_id` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Request body Fields: `display_name`, `lifecycle_stage`, `owner_user_id`, `source`, `tags` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Update verified or is_primary on an identifier. (/docs/developers/api-reference/crm/patch_identifier_endpoint_api_crm_v1_identifiers__identifier_id__patch) ## PATCH /api/crm/v1/identifiers/{identifier_id} **Update verified or is_primary on an identifier.** Update the `verified` flag or promote an identifier to primary status. At most one primary identifier of each kind is allowed per contact. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `identifier_id` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Request body Fields: `is_primary`, `verified` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Update a note (author or admin only). (/docs/developers/api-reference/crm/patch_note_endpoint_api_crm_v1_notes__note_id__patch) ## PATCH /api/crm/v1/notes/{note_id} **Update a note (author or admin only).** Edit the body of an existing note. Only the original author or a user with the `crm:admin` scope may update a note. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `note_id` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Request body Fields: `body` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Replace the active CRM config (creates or bumps version). (/docs/developers/api-reference/crm/put_config_me_endpoint_api_crm_v1_config_me_put) ## PUT /api/crm/v1/config/me **Replace the active CRM config (creates or bumps version).** Validates the supplied document and persists it as the active config; the prior version is snapshotted into crm_config_history. Returns 422 with code='invalid_config' if the document fails schema validation. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Request body Fields: object ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Restore a prior version of the CRM config as the new active. (/docs/developers/api-reference/crm/rollback_config_me_endpoint_api_crm_v1_config_me_rollback_post) ## POST /api/crm/v1/config/me/rollback **Restore a prior version of the CRM config as the new active.** Looks up the requested version in crm_config_history and writes it as a new active config (version = current+1). The previously-active config is itself snapshotted into history. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `version` | query | yes | integer | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Search contacts by free-text query against their notes / interactions / extensions. (/docs/developers/api-reference/crm/search_contacts_endpoint_api_crm_v1_contacts_search_get) ## GET /api/crm/v1/contacts/search **Search contacts by free-text query against their notes / interactions / extensions.** Calls Reeve.Memory's per-org index (DEV-535) and joins matching contacts. Returns 503 search_unavailable if Memory is down (read path stays available). ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `q` | query | yes | string | | | `top_k` | query | no | integer | | | `kinds` | query | no | | Comma-separated subset of {note,interaction,extension} | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Upsert a contact (/docs/developers/api-reference/crm/upsert_contact_endpoint_api_crm_v1_contacts_post) ## POST /api/crm/v1/contacts **Upsert a contact** Create or update a contact, dedupe by normalized identifier within the org. Returns 201 if new, 200 if existing was updated. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Request body Fields: `contact_type` (required), `display_name`, `extensions`, `identifiers`, `lifecycle_stage`, `owner_user_id`, `source`, `tags` ### Responses | Status | Description | | --- | --- | | 201 | Successful Response | | 422 | Validation Error | # Upsert an extension on a contact (idempotent by type). (/docs/developers/api-reference/crm/upsert_extension_endpoint_api_crm_v1_contacts__contact_id__extensions__extension_type__put) ## PUT /api/crm/v1/contacts/{contact_id}/extensions/{extension_type} **Upsert an extension on a contact (idempotent by type).** Create or replace a typed extension blob on a contact. Each (contact, host_app, extension_type) triple is unique; calling again with a new payload overwrites the existing record. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `contact_id` | path | yes | string | | | `extension_type` | path | yes | string | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Request body Fields: `payload` (required), `schema_version` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Accept all suggestions for a job (/docs/developers/api-reference/enrich/accept_all_for_job_api_enrich_v1_jobs__job_id__accept_all_post) ## POST /api/enrich/v1/jobs/{job_id}/accept_all **Accept all suggestions for a job** Bulk-apply every pending suggestion. Returns per-suggestion accept/fail lists. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `job_id` | path | yes | string | | | `X-Org-Id` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `x-guest-id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Accept a suggestion (/docs/developers/api-reference/enrich/accept_suggestion_api_enrich_v1_suggestions__suggestion_id__accept_post) ## POST /api/enrich/v1/suggestions/{suggestion_id}/accept **Accept a suggestion** Dispatch acceptance to the registered handler for the suggestion's target_kind. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `suggestion_id` | path | yes | string | | | `X-Org-Id` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `x-guest-id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Dismiss a suggestion (/docs/developers/api-reference/enrich/dismiss_suggestion_endpoint_api_enrich_v1_suggestions__suggestion_id__dismiss_post) ## POST /api/enrich/v1/suggestions/{suggestion_id}/dismiss **Dismiss a suggestion** Mark a pending suggestion as rejected; no writeback fires. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `suggestion_id` | path | yes | string | | | `X-Org-Id` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `x-guest-id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Enqueue an enrichment job (/docs/developers/api-reference/enrich/enqueue_job_api_enrich_v1_jobs_post) ## POST /api/enrich/v1/jobs **Enqueue an enrichment job** Schedule a recipe-driven run for any target entity. Idempotent on Idempotency-Key. Results land as suggestions on /api/enrich/v1/suggestions. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Org-Id` | header | no | | | | `Idempotency-Key` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `x-guest-id` | header | no | | | ### Request body Fields: `budget_usd`, `recipe_id`, `recipe_override`, `target` (required) ### Responses | Status | Description | | --- | --- | | 202 | Successful Response | | 400 | Bad Request | | 401 | Unauthorized | | 403 | Forbidden | | 409 | Conflict | | 422 | Validation Error | # Get artifact presigned URL (/docs/developers/api-reference/enrich/get_artifact_url_api_enrich_v1_artifacts__artifact_id__get) ## GET /api/enrich/v1/artifacts/{artifact_id} **Get artifact presigned URL** Return a 5-minute presigned S3 URL for an enrichment artifact. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `artifact_id` | path | yes | string | | | `X-Org-Id` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `x-guest-id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get an enrichment job (/docs/developers/api-reference/enrich/get_job_api_enrich_v1_jobs__job_id__get) ## GET /api/enrich/v1/jobs/{job_id} **Get an enrichment job** Fetch a single job's status, telemetry, cost, and target spec. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `job_id` | path | yes | string | | | `X-Org-Id` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `x-guest-id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # List enrichment jobs (/docs/developers/api-reference/enrich/list_jobs_api_enrich_v1_jobs_get) ## GET /api/enrich/v1/jobs **List enrichment jobs** List recent jobs filtered by `host_app`, `target_kind`, `target_id`. Most-recent first, capped at 50. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `host_app` | query | no | | | | `target_kind` | query | no | | | | `target_id` | query | no | | | | `X-Org-Id` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `x-guest-id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # List enrichment suggestions (/docs/developers/api-reference/enrich/list_suggestions_api_enrich_v1_suggestions_get) ## GET /api/enrich/v1/suggestions **List enrichment suggestions** List suggestions filtered by `host_app`, `target_kind`, `target_id`, and optionally `status`. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `host_app` | query | no | | | | `target_kind` | query | no | | | | `target_id` | query | no | | | | `status` | query | no | | | | `X-Org-Id` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | | `x-user-id` | header | no | | | | `authorization` | header | no | | | | `x-guest-id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Choropleth Spec Endpoint (/docs/developers/api-reference/maps/choropleth_spec_endpoint_api_maps_v1_choropleth_spec_post) ## POST /api/maps/v1/choropleth-spec **Choropleth Spec Endpoint** Classify a list of numeric values (quantile or equal-interval) into a Mapbox 'step' paint expression and a per-class color legend. ### Request body Fields: `breaks`, `classes`, `method`, `property_name`, `scheme`, `values` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Geocode Endpoint (/docs/developers/api-reference/maps/geocode_endpoint_api_maps_v1_geocode_post) ## POST /api/maps/v1/geocode **Geocode Endpoint** Forward-geocode a place or address query into ranked coordinate results, filtered by country, proximity, and place types. ### Request body Fields: `country`, `limit`, `proximity`, `query` (required), `types` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get Config (/docs/developers/api-reference/maps/get_config_api_maps_v1_config_get) ## GET /api/maps/v1/config **Get Config** Return the tenant's interactive basemap configuration (Mapbox style, URL-restricted public token, default center/zoom, attribution) and meter one map load. ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | # Reverse Geocode Endpoint (/docs/developers/api-reference/maps/reverse_geocode_endpoint_api_maps_v1_reverse_geocode_post) ## POST /api/maps/v1/reverse-geocode **Reverse Geocode Endpoint** Reverse-geocode a latitude/longitude pair into the nearest matching place results. ### Request body Fields: `lat` (required), `lng` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Static Image (/docs/developers/api-reference/maps/static_image_api_maps_v1_static_image_post) ## POST /api/maps/v1/static-image **Static Image** Proxy a Mapbox Static Images request and return PNG bytes. The Mapbox secret token is never forwarded to the client — only the PNG bytes are returned. Requires the 'maps' capability on the host-app credential (X-Reeve-Host-Key). ### Request body Fields: `center`, `overlays`, `padding`, `pins`, `retina`, `size` (required), `style`, `zoom` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Upload File (/docs/developers/api-reference/storage/upload_file_api_storage_v1_files_post) ## POST /api/storage/v1/files **Upload File** DEV-3739: host-key file upload → public Drive URL. multipart `file` → `{ "url": "https://drive.meetreeve.com///" }`. The object is namespaced by a per-host-app key prefix (no cross-tenant access); the URL is returned only after the bytes are durably written. ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Add Kg Edge (/docs/developers/api-reference/memory/add_kg_edge_api_memory_v1_kg_edges_post) ## POST /api/memory/v1/kg/edges **Add Kg Edge** Create a directed knowledge-graph edge between two nodes. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Request body Fields: `from` (required), `kind` (required), `namespace` (required), `principal_id`, `props`, `to` (required), `valid_from`, `valid_to`, `visibility` ### Responses | Status | Description | | --- | --- | | 201 | Successful Response | | 422 | Validation Error | # Add Kg Node (/docs/developers/api-reference/memory/add_kg_node_api_memory_v1_kg_nodes_post) ## POST /api/memory/v1/kg/nodes **Add Kg Node** Create a knowledge-graph node in the namespace. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Request body Fields: `id` (required), `kind` (required), `namespace` (required), `principal_id`, `props`, `visibility` ### Responses | Status | Description | | --- | --- | | 201 | Successful Response | | 422 | Validation Error | # Composite Query Endpoint (/docs/developers/api-reference/memory/composite_query_endpoint_api_memory_v1_composite_query_post) ## POST /api/memory/v1/composite_query **Composite Query Endpoint** Cross-namespace hybrid FTS + vector query, RRF-fused, scope-filtered. Mirrors the auth convention of POST /api/memory/v1/query -- caller provides X-Reeve-Host-App; scope visibility is encoded inside `body.scopes`. The upstream `_resolve_namespaces` filter only accepts namespaces matching `host_app + visibility (+ principal_id)`, which keeps cross-tenant leakage out of the result set. DEV-1247: for host-app principals, personal-scope results require an explicit ``principal_id`` query param. The service's _resolve_namespaces uses ``caller_principal_id`` (not ``scope.principal_id`` from the body) to pin personal namespace queries -- prevents body forgery. For a host-app without an asserted principal_id, personal scopes are silently dropped (safe, fail-closed). Normal users are unaffected. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `principal_id` | query | no | | | | `X-Reeve-Host-App` | header | no | | | ### Request body Fields: `include_markers`, `include_score`, `query` (required), `rrf_k`, `scopes` (required), `token_budget`, `top_k` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Create Namespace (/docs/developers/api-reference/memory/create_namespace_api_memory_v1_namespaces_post) ## POST /api/memory/v1/namespaces **Create Namespace** Create a memory namespace (a logical store) for the calling host-app/tenant. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Request body Fields: `embedding_dims`, `embedding_model`, `lane`, `name` (required), `principal_id`, `visibility` ### Responses | Status | Description | | --- | --- | | 201 | Successful Response | | 422 | Validation Error | # Delete Item Endpoint (/docs/developers/api-reference/memory/delete_item_endpoint_api_memory_v1_items__namespace_id___item_id__delete) ## DELETE /api/memory/v1/items/{namespace_id}/{item_id} **Delete Item Endpoint** Delete an item and cascade to its children via the lifecycle forget walker. Returns 404 if the item doesn't exist or belongs to another user. DEV-1247: host-app principals must supply ``principal_id`` as a query param. Normal users are unaffected (``ctx.user_id`` is used). ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `namespace_id` | path | yes | string | | | `item_id` | path | yes | string | | | `principal_id` | query | no | | | | `X-Reeve-Host-App` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Delete Namespace (/docs/developers/api-reference/memory/delete_namespace_api_memory_v1_namespaces__namespace_id__delete) ## DELETE /api/memory/v1/namespaces/{namespace_id} **Delete Namespace** Archive a namespace by id, scope-checked. Authz: caller's (host_app, org_id) must match the namespace's (host_app, org_id); universal namespaces additionally require a service token. Without this check anyone authenticated could delete any namespace cross-tenant by guessing/leaking the id. Personal-tier (DEV-1247): the acting_principal is ctx.user_id for normal users; for host-app principals (ctx.user_id is None) it is the explicit ``principal_id`` query param. A host-app may only delete a personal namespace whose principal_id matches the one it asserts. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `namespace_id` | path | yes | string | | | `principal_id` | query | no | | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Responses | Status | Description | | --- | --- | | 204 | Successful Response | | 422 | Validation Error | # Feedback Signal Endpoint (/docs/developers/api-reference/memory/feedback_signal_endpoint_api_memory_v1_feedback_signal_post) ## POST /api/memory/v1/feedback_signal **Feedback Signal Endpoint** Submit one rating/observation against a memory item. Behavior: - INSERT into memory_signals (processed_at NULL). - INSERT into memory_journal (operation='signal'). - On Postgres: pg_notify('memory_signals', {namespace_id, item_id}). The aggregator LISTEN worker picks it up and recomputes the score. - On SQLite: notify is skipped (tests call process_signal directly). DEV-1247: host-app principals operating against a personal-tier item must supply ``principal_id`` as a query param. The service guard (feedback._verify_item_access) uses ``if not caller_principal_id: deny`` for personal namespaces -- previously passing None for a host-app call would incorrectly 403. Now the asserted principal is threaded through so legitimate host-app personal feedback works and cross-principal access remains forbidden. Normal users are unaffected. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `principal_id` | query | no | | | | `X-Reeve-Host-App` | header | no | | | ### Request body Fields: `item_id` (required), `metadata`, `namespace_id` (required), `signal_kind` (required), `value_bool`, `value_float`, `value_text` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Forget All Endpoint (/docs/developers/api-reference/memory/forget_all_endpoint_api_memory_v1_forget_all_post) ## POST /api/memory/v1/forget-all **Forget All Endpoint** Delete ALL personal memory items for the authenticated caller. Safety guard: body must include `confirm: true`. Omitting it or passing false returns 400 without touching any data. DEV-1247: host-app principals must supply ``principal_id`` as a query param (personal-scope operation). Normal users are unaffected. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `principal_id` | query | no | | | | `X-Reeve-Host-App` | header | no | | | ### Request body Fields: `confirm` (required), `scope` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Forget Endpoint (/docs/developers/api-reference/memory/forget_endpoint_api_memory_v1_forget_post) ## POST /api/memory/v1/forget **Forget Endpoint** Application-walker forget over the parent_item_id chain. Scope semantics (at least one of these must be effectively set): - body.item_id -> forget exactly this item + its descendants - body.namespace_id -> forget all items in this namespace - else: requires (owner_principal_id OR origin_org_id) to resolve target namespaces via (host_app, scope, owner) The walker respects two safety guards (always-on): 1. Cross-org boundary -- children whose origin_org_id differs from the caller's org_id are skipped (and their subtree NOT walked). 2. Pin discipline (opt-in) -- pass hard_delete=False to leave pinned items intact (compaction caller pattern). Default is hard_delete=True which overrides pin (explicit user forget). DEV-1247: for host-app principals, personal-scope forget requires body.owner_principal_id to be set (fail-closed 400 if absent). Normal users are unaffected. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | ### Request body Fields: `actor_id`, `actor_kind`, `hard_delete`, `item_id`, `namespace_id`, `origin_org_id`, `owner_principal_id` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get Settings Endpoint (/docs/developers/api-reference/memory/get_settings_endpoint_api_memory_v1_settings_get) ## GET /api/memory/v1/settings **Get Settings Endpoint** Return the authenticated caller's memory settings. Falls back to default values (auto_write_enabled=True, retention_prefs={}) when the caller has never written settings before. DEV-1247: host-app principals must supply ``principal_id`` as a query param. Without it a 400 is returned (fail-closed -- principal_id IS the primary key of memory_user_settings; a NULL-keyed read is nonsensical). Normal users are unaffected. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `principal_id` | query | no | | | | `X-Reeve-Host-App` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get Usage (/docs/developers/api-reference/memory/get_usage_api_memory_v1_usage_get) ## GET /api/memory/v1/usage **Get Usage** Return memory usage counters (items, namespaces, storage) for the caller. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `day` | query | no | | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Ingest Corpus Doc (/docs/developers/api-reference/memory/ingest_corpus_doc_api_memory_v1_corpus_ingest_post) ## POST /api/memory/v1/corpus/ingest **Ingest Corpus Doc** Ingest one corpus document. Returns the doc id + ingest status. The schema validator guarantees exactly one of content_text / content_b64 is set. Text is UTF-8 encoded; b64 is strictly decoded (400 on invalid b64). The chunk+embed work runs in a separate fire-and-forget task when the doc is new or its content changed (`start.needs_embed`). ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | ### Request body Fields: `content_b64`, `content_text`, `metadata`, `mime`, `namespace` (required), `source_kind` (required), `source_uri` (required) ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # List Items Endpoint (/docs/developers/api-reference/memory/list_items_endpoint_api_memory_v1_items_get) ## GET /api/memory/v1/items **List Items Endpoint** List memory items in the caller's scope. scope='personal' (default) -- the caller's own items, keyed by principal_id. scope='tenant' -- the caller org's tenant-visibility items (brand DNA + studio:threads), keyed by ctx.organization_id (DEV-1999). Cursor-based pagination: pass `cursor=` to get the next page. Returns {items, cursor, total}. DEV-1247: host-app principals must supply ``principal_id`` as a query param. Normal users are unaffected (``ctx.user_id`` is used). ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `scope` | query | no | string | | | `lane` | query | no | | | | `tier` | query | no | | | | `namespace_id` | query | no | | | | `cursor` | query | no | | | | `limit` | query | no | integer | | | `principal_id` | query | no | | | | `X-Reeve-Host-App` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # List Markers Endpoint (/docs/developers/api-reference/memory/list_markers_endpoint_api_memory_v1_markers__namespace_id___item_id__get) ## GET /api/memory/v1/markers/{namespace_id}/{item_id} **List Markers Endpoint** List all markers on a single item. DEV-1247: host-app principals must supply ``principal_id`` as a query param when reading markers on a personal-tier item. Normal users are unaffected. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `namespace_id` | path | yes | string | | | `item_id` | path | yes | string | | | `principal_id` | query | no | | | | `X-Reeve-Host-App` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # List Namespaces (/docs/developers/api-reference/memory/list_namespaces_api_memory_v1_namespaces_get) ## GET /api/memory/v1/namespaces **List Namespaces** List namespaces visible to the caller. Personal-tier filtering for host-app principals (DEV-1247): - If ``principal_id`` is asserted AND the caller is a host-app, only personal namespaces for that principal_id are returned. - If no ``principal_id`` is asserted by a host-app, all personal namespaces are excluded (host-apps cannot enumerate across principals). - Normal user callers are unaffected (ctx.host_app_id is None). ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `include_universal` | query | no | boolean | | | `principal_id` | query | no | | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Patch Item Endpoint (/docs/developers/api-reference/memory/patch_item_endpoint_api_memory_v1_items__namespace_id___item_id__patch) ## PATCH /api/memory/v1/items/{namespace_id}/{item_id} **Patch Item Endpoint** Update text, importance, or payload on an item. Also sets the `verified` marker to record that a human reviewed + confirmed this item. Returns 404 if the item doesn't exist or belongs to another user. DEV-1247: host-app principals must supply ``principal_id`` as a query param. Normal users are unaffected (``ctx.user_id`` is used). ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `namespace_id` | path | yes | string | | | `item_id` | path | yes | string | | | `principal_id` | query | no | | | | `X-Reeve-Host-App` | header | no | | | ### Request body Fields: `importance`, `payload`, `text` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Patch Settings Endpoint (/docs/developers/api-reference/memory/patch_settings_endpoint_api_memory_v1_settings_patch) ## PATCH /api/memory/v1/settings **Patch Settings Endpoint** Update the authenticated caller's memory settings. All fields are optional -- only supplied fields are changed. DEV-1247: host-app principals must supply ``principal_id`` as a query param. Without it a 400 is returned (fail-closed -- writing a settings row with principal_id=NULL would corrupt the PRIMARY KEY). Normal users are unaffected. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `principal_id` | query | no | | | | `X-Reeve-Host-App` | header | no | | | ### Request body Fields: `auto_write_enabled`, `retention_prefs` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Query (/docs/developers/api-reference/memory/query_api_memory_v1_query_post) ## POST /api/memory/v1/query **Query** Hybrid semantic + lexical search over a namespace; returns ranked memory items. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Request body Fields: `as_of`, `filter`, `include_kg`, `namespace` (required), `principal_id`, `rerank`, `text` (required), `top_k`, `visibility` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Remove Marker Endpoint (/docs/developers/api-reference/memory/remove_marker_endpoint_api_memory_v1_markers__namespace_id___item_id___marker__delete) ## DELETE /api/memory/v1/markers/{namespace_id}/{item_id}/{marker} **Remove Marker Endpoint** Remove a marker. Idempotent -- no-op if the marker isn't set. DEV-1247: host-app principals must supply ``principal_id`` as a query param when operating on a personal-tier item. Normal users are unaffected. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `namespace_id` | path | yes | string | | | `item_id` | path | yes | string | | | `marker` | path | yes | string | | | `principal_id` | query | no | | | | `X-Reeve-Host-App` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Research Submit (/docs/developers/api-reference/memory/research_submit_api_memory_v1_research_submit_post) ## POST /api/memory/v1/research/submit **Research Submit** Service-token gated. Upstream research pipeline writes one curated item. Stricter than /upsert: visibility ∈ {universal, tenant} only; service token required regardless of visibility; designed to be called by the Opus + reviewer pipeline that produces the cited entries. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Request body Fields: `item` (required), `namespace` (required), `org_id`, `visibility` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Set Marker Endpoint (/docs/developers/api-reference/memory/set_marker_endpoint_api_memory_v1_markers_post) ## POST /api/memory/v1/markers **Set Marker Endpoint** Set a marker on an item. Idempotent -- repeated calls are a no-op. DEV-1247: host-app principals must supply ``principal_id`` as a query param when operating on a personal-tier item. Absent assertion for a host-app -> 400 (fail-closed). Normal users are unaffected. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `principal_id` | query | no | | | | `X-Reeve-Host-App` | header | no | | | ### Request body Fields: `item_id` (required), `marker` (required), `namespace_id` (required), `set_by` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Traverse Kg (/docs/developers/api-reference/memory/traverse_kg_api_memory_v1_kg_traverse_get) ## GET /api/memory/v1/kg/traverse **Traverse Kg** Traverse the knowledge graph from a starting node, following edges up to a given depth. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `namespace` | query | yes | string | | | `start` | query | yes | string | | | `depth` | query | no | integer | | | `as_of` | query | no | | | | `edge_kinds` | query | no | | | | `visibility` | query | no | string | | | `principal_id` | query | no | | | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Upsert (/docs/developers/api-reference/memory/upsert_api_memory_v1_upsert_post) ## POST /api/memory/v1/upsert **Upsert** Insert or update memory items in a namespace; text is embedded for hybrid (vector + lexical) retrieval. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `X-Reeve-Host-App` | header | no | | | | `X-Org-Id` | header | no | | | | `X-Reeve-Service-Token` | header | no | | | ### Request body Fields: `items` (required), `namespace` (required), `principal_id`, `visibility` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Context Webhook Endpoint (/docs/developers/api-reference/voice/context_webhook_endpoint_api_voice_v1_context_post) ## POST /api/voice/v1/context **Context Webhook Endpoint** Telnyx dynamic_variables_webhook_url — called at CONVERSATION START. Returns per-call context (caller identity + live availability + facility info) wrapped under a top-level `dynamic_variables` object (Telnyx requires this), so the fast model skips a mid-call tool round-trip. Task 13 live-pin: confirm exact dynamic-variables webhook request shape (assistant_id + caller number field paths). We parse defensively across the shapes Telnyx may send and pull the assistant id + caller number. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `x-reeve-voice-secret` | header | no | | | ### Request body Fields: object ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Create Agent Endpoint (/docs/developers/api-reference/voice/create_agent_endpoint_api_voice_v1_agents_post) ## POST /api/voice/v1/agents **Create Agent Endpoint** Provision a voice agent (Telnyx assistant) and persist it. ### Request body Fields: `dispatch_url`, `instructions` (required), `name`, `phone_number`, `provider`, `provider_config` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Create Call Endpoint (/docs/developers/api-reference/voice/create_call_endpoint_api_voice_v1_calls_post) ## POST /api/voice/v1/calls **Create Call Endpoint** Place an outbound call from a provisioned agent and bind the SID. ### Request body Fields: `agent_id` (required), `contact_id`, `from_number`, `idempotency_key`, `to_number` (required), `variables` ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get Agent Endpoint (/docs/developers/api-reference/voice/get_agent_endpoint_api_voice_v1_agents__agent_id__get) ## GET /api/voice/v1/agents/{agent_id} **Get Agent Endpoint** Fetch a voice agent's configuration by ID. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `agent_id` | path | yes | string | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error | # Get Call Endpoint (/docs/developers/api-reference/voice/get_call_endpoint_api_voice_v1_calls__call_id__get) ## GET /api/voice/v1/calls/{call_id} **Get Call Endpoint** Fetch a voice call record (status, transcript metadata) by ID. ### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `call_id` | path | yes | string | | ### Responses | Status | Description | | --- | --- | | 200 | Successful Response | | 422 | Validation Error |