v1.0.0
OpenAPI 3.1.0

AgencyTitan API

The AgencyTitan public REST API is organized around resource-oriented URLs and standard HTTP verbs. It accepts JSON request bodies, returns JSON responses, and uses conventional HTTP status codes.

The base URL for all endpoints is https://api.agencytitan.com. Authenticate with either OAuth 2.0 or an API key (equal first-class paths — see Authentication). For external AI agents, AgencyTitan hosts an MCP server (External AI via MCP); see the MCP guide.

Server:https://api.agencytitan.com

Production

Client Libraries

Authentication (Collapsed)

All /v1 endpoints (except /v1/openapi.json and /v1/llms.txt) authenticate with a bearer token, sent as an Authorization: Bearer <token> header. Two credential types are equal first-class paths:

  • OAuth 2.0 access tokens — issued through the OAuth authorization flow. Preferred by Claude.ai, ChatGPT, and other MCP hosts that discover OAuth automatically.
  • API keys — start with at_, created under Settings → System → API & MCP. Preferred by Cursor, Claude Code, and clients that set request headers.

Each API key is assigned to a service account; every request executes as that service account, with its permissions, not as the person who created the key. OAuth 2.0 tokens act as the authenticated user instead. Keep credentials secret: never expose them in client-side code or public repositories.

Send the token as an Authorization header on every request:

curl https://api.agencytitan.com/v1/clients \
  -H "Authorization: Bearer at_your_api_key_here"
const res = await fetch("https://api.agencytitan.com/v1/clients", {
  headers: { Authorization: "Bearer at_your_api_key_here" },
});
const data = await res.json();
import requests

res = requests.get(
    "https://api.agencytitan.com/v1/clients",
    headers={"Authorization": "Bearer at_your_api_key_here"},
)
data = res.json()
$ch = curl_init("https://api.agencytitan.com/v1/clients");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer at_your_api_key_here"]);
$data = json_decode(curl_exec($ch), true);
require "net/http"
require "json"

uri = URI("https://api.agencytitan.com/v1/clients")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer at_your_api_key_here"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
data = JSON.parse(res.body)

A missing, invalid, expired, or revoked token returns 401 with an OAuth-style body:

{
  "error": "invalid_token",
  "error_description": "The access token is invalid."
}

Requests (Collapsed)

Send requests to the base URL https://api.agencytitan.com over HTTPS. The API is resource-oriented and uses standard verbs: GET to read, POST to create, PUT to set a desired state, PATCH to update, and DELETE to remove.

Request bodies are JSON. Send a Content-Type: application/json header on POST, PUT, and PATCH requests. Path parameters are described by each operation.

Requests are strictly validated: unknown query parameters or body properties, and out-of-range values, are rejected with a 400 (see Errors). Send only the documented fields.

curl -X POST https://api.agencytitan.com/v1/clients \
  -H "Authorization: Bearer at_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Acme Co", "profile_id": "123e4567-e89b-12d3-a456-426614174000" }'
const res = await fetch("https://api.agencytitan.com/v1/clients", {
  method: "POST",
  headers: {
    Authorization: "Bearer at_your_api_key_here",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Acme Co",
    profile_id: "123e4567-e89b-12d3-a456-426614174000",
  }),
});
const data = await res.json();
import requests

res = requests.post(
    "https://api.agencytitan.com/v1/clients",
    headers={"Authorization": "Bearer at_your_api_key_here"},
    json={"name": "Acme Co", "profile_id": "123e4567-e89b-12d3-a456-426614174000"},
)
data = res.json()

Responses (Collapsed)

Every response is JSON wrapped in a data envelope. Single-item endpoints return the object directly under data. List endpoints return an array under data alongside a pagination object (see Pagination). The examples show each shape.

Single item

{
  "data": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "name": "Acme Co"
  }
}

List

{
  "data": [
    {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "name": "Acme Co"
    }
  ],
  "pagination": {
    "total": 120,
    "limit": 50,
    "offset": 0
  }
}

Successful reads and writes return 200. Failures use conventional HTTP status codes and a consistent error body (see Errors).

Errors (Collapsed)

The API uses conventional HTTP status codes: 2xx for success, 4xx for a problem with the request, and 5xx for an error on our side. Requests are strictly validated: unknown or out-of-range query parameters and body properties are rejected with a 400.

Most errors return the standard envelope, whose type field you can branch on. The type is one of validation_error, permission_denied, not_found, conflict, rate_limited, or internal. Authentication failures (401) use an OAuth-style shape instead. Both shapes are shown alongside.

Error envelope

{
  "error": {
    "type": "validation_error",
    "message": "Human-readable description."
  }
}

Authentication error (401)

{
  "error": "invalid_token",
  "error_description": "..."
}

Status codes: 400 validation failed, 401 missing or invalid token, 403 insufficient permission, 404 not found, 409 conflict, 429 rate limited, 500 internal error.

Pagination (Collapsed)

List endpoints use offset pagination via the limit (1-200, default 50) and offset (default 0) query parameters, alongside sort and order. Request the next page by advancing offset by limit.

curl "https://api.agencytitan.com/v1/clients?limit=50&offset=0" \
  -H "Authorization: Bearer at_your_api_key_here"
const res = await fetch(
  "https://api.agencytitan.com/v1/clients?limit=50&offset=0",
  { headers: { Authorization: "Bearer at_your_api_key_here" } },
);
const data = await res.json();
import requests

res = requests.get(
    "https://api.agencytitan.com/v1/clients",
    params={"limit": 50, "offset": 0},
    headers={"Authorization": "Bearer at_your_api_key_here"},
)
data = res.json()

A list response carries the data array plus a pagination object with the total count. Single-item endpoints return { "data": { ... } } with no pagination.

Pagination response

{
  "data": [ ... ],
  "pagination": {
    "total": 120,
    "limit": 50,
    "offset": 0
  }
}

Sorting (Collapsed)

List endpoints accept a sort and an order parameter. sort selects the field to order by. The allowed values differ per resource, so check the sort parameter on each list endpoint. order is either asc or desc (defaults to desc).

curl "https://api.agencytitan.com/v1/clients?sort=name&order=asc" \
  -H "Authorization: Bearer at_your_api_key_here"
const res = await fetch(
  "https://api.agencytitan.com/v1/clients?sort=name&order=asc",
  { headers: { Authorization: "Bearer at_your_api_key_here" } },
);
const data = await res.json();
import requests

res = requests.get(
    "https://api.agencytitan.com/v1/clients",
    params={"sort": "name", "order": "asc"},
    headers={"Authorization": "Bearer at_your_api_key_here"},
)
data = res.json()

Filtering (Collapsed)

List endpoints support filtering. Where a search parameter is available it performs a free-text match (for example over a client's name and email). Resource-specific filters (such as status, priority_level, tags, or profile_id) are documented on each list endpoint.

Array filters accept multiple values: repeat the parameter to match any of several values (logical OR). Different query parameters are AND. On tasks and task-stages, first-class filters are also AND-merged with filter_group when both are sent.

curl "https://api.agencytitan.com/v1/clients?search=acme&status=active_client&status=lead" \
  -H "Authorization: Bearer at_your_api_key_here"
const params = new URLSearchParams({ search: "acme" });
params.append("status", "active_client");
params.append("status", "lead");
const res = await fetch(
  `https://api.agencytitan.com/v1/clients?${params}`,
  { headers: { Authorization: "Bearer at_your_api_key_here" } },
);
const data = await res.json();
import requests

res = requests.get(
    "https://api.agencytitan.com/v1/clients",
    params={"search": "acme", "status": ["active_client", "lead"]},
    headers={"Authorization": "Bearer at_your_api_key_here"},
)
data = res.json()

Task FilterGroup (filter_group)

GET /v1/tasks and GET /v1/task-stages also accept an advanced filter_group for nested AND/OR trees and custom fields. Prefer first-class params (priority_level, tags, status_scope, assignees, dates) when they are enough; those are AND-merged with filter_group when both are sent.

Pass filter_group as one URL-encoded JSON string:

{
  "logical_operator": "AND",
  "conditions": [
    { "field": "priority_level", "operator": "in_list", "value": ["high", "urgent"] },
    { "field": "field_values.budget", "operator": "greater_than", "value": "5000" }
  ],
  "groups": []
}
FILTER='{"logical_operator":"AND","conditions":[{"field":"field_values.budget","operator":"greater_than","value":"5000"}],"groups":[]}'
curl -G "https://api.agencytitan.com/v1/tasks" \
  -H "Authorization: Bearer at_your_api_key_here" \
  --data-urlencode "filter_group=${FILTER}"
const filterGroup = {
  logical_operator: "AND",
  conditions: [
    { field: "field_values.budget", operator: "greater_than", value: "5000" },
  ],
  groups: [],
};
const params = new URLSearchParams();
params.set("filter_group", JSON.stringify(filterGroup));
const res = await fetch(
  `https://api.agencytitan.com/v1/tasks?${params}`,
  { headers: { Authorization: "Bearer at_your_api_key_here" } },
);

System fields include status, priority_level, tags, assigned_to, client_id, process_definition_id, department_id, and date fields (due_date, created_at, and related). Custom fields use field_values.<field_key> (discover keys with GET /v1/fields). Option values come from GET /v1/tasks/options / GET /v1/tenant-context. See the filter_group parameter on GET /v1/tasks and GET /v1/task-stages for the full operator list.

Rate limits (Collapsed)

Requests are rate limited per tenant to 600 requests per minute across all /v1 REST endpoints. Exceeding the limit returns a 429 response; the Retry-After header indicates how many seconds to wait before retrying.

Activity (Collapsed)

Permissioned tenant audit activity, field history, and application events.

Activity Operations

Activity Events (Collapsed)

Application lifecycle events for currently readable resources.

Activity Events Operations

Agency Fields (Collapsed)

Tenant-global custom-field values for the agency.

AI Memory (Collapsed)

Explicit agency and personal information used to tailor AI responses.

AI Presets (Collapsed)

AI preset discovery, canonical detail reads, and approval-gated deletion.

AI Requests (Collapsed)

Prompt-free AI request metadata, costs, status, and safe errors.

API Request Logs (Collapsed)

Credential-bound, payload-free public API request observability.

Assignment Feedback Rules (Collapsed)

Tenant-wide AI assignment feedback rules.

Assignment Feedback Rules Operations

Assignment History (Collapsed)

Task and ticket assignment decisions and governed reruns.

Attachments (Collapsed)

Metadata and short-lived download access for files attached to readable tenant entities.

Auth Events (Collapsed)

Bounded authentication security events without raw network identifiers.

Auth Events Operations

Automation Authoring (Collapsed)

Automation authoring catalogs and graph lifecycle operations for creating, editing, organizing, and publishing automations.

Automation Authoring Operations

Automation Execution Items (Collapsed)

Step-level automation execution outcomes.

Automation Execution Items Operations

Automation Tests (Collapsed)

Governed direct-step probes, durable full automation test runs, progress, results, and safe sample hydration.

Billing Payment Methods (Collapsed)

Approval-ready management of client-owned payment methods; tenant platform-charge methods are excluded.

Billing Payment Methods Operations

Billing Payment Providers (Collapsed)

Approval-ready client-payment processor selection, capabilities, and allowlisted metadata.

Billing Payment Providers Operations

Billing Payment Settings (Collapsed)

Wallet and method-type presentation settings for client checkout.

Billing Payment Settings Operations

Billing Settings (Collapsed)

Tenant auto-reload and billing contact settings, excluding platform charge-method replacement.

Billing Settings Operations

Certificate Templates (Collapsed)

Certificate templates issued for completed training and learning paths.

Chat Messages (Collapsed)

Checklist Progress (Collapsed)

Caller-owned completion state for interactive SOP checklists.

Client Billing Settings (Collapsed)

Per-client billing behavior and financial-notification recipients.

Client Note Folders (Collapsed)

Folders that organize notes within one client record.

Client Profile Folders (Collapsed)

Client Program Processes (Collapsed)

Processes attached to a client program, including effective schedules and exclusion state.

Client Program Processes Operations

Client Program Schedules (Collapsed)

Automatic task-generation schedules attached to processes within program definitions.

Client Program Schedules Operations

Cloud Files (Collapsed)

Server-side import of cloud-provider files into persistent tenant storage without exposing file payloads.

Cloud Files Operations

Custom Object Folders (Collapsed)

Folders that organize custom object definitions, including governed rehome-on-delete behavior.

Custom Object Relationships (Collapsed)

Relationship definitions between custom object types, used to create record links.

Custom Object Relationships Operations

Department Levels (Collapsed)

Department Members (Collapsed)

Active and pending members assigned to agency departments and levels.

Department Members Operations

Departments (Collapsed)

Your agency's departments and their configured levels.

Email Delivery Logs (Collapsed)

Operational email-delivery metadata and explicitly permissioned content.

Employee Compensation (Collapsed)

Effective-dated pay records for agency members. New records trim prior ones so pay history stays intact.

Employee Compensation Operations

Employee Schedules (Collapsed)

Working schedules for agency members: one schedule per member covering days, hours, and timezone.

Employee Schedules Operations

Entity Layouts (Collapsed)

Read-only layout structures for task stages, client profiles, views, modals, and forms.

Entity Pins (Collapsed)

Personal and tenant-wide pins for visible tenant entities.

Escalation Settings (Collapsed)

Tenant routing, notification, and visibility settings for escalations.

Field Folders (Collapsed)

Field History (Collapsed)

Clean field-level history for currently readable resources.

Field History Operations

Form Submissions (Collapsed)

Submitted form responses, including the submitted field values.

Images (Collapsed)

Non-destructive crop and resize operations over tenant-owned raster images.

Invoices (Collapsed)

Client invoices: drafts, line items, status transitions, and recorded payments.

Knowledge Base (Collapsed)

Self-scoped knowledge-base engagement statistics.

Knowledge Base Operations

Layouts (Collapsed)

Atomic, revision-safe patching and publication of canonical entity layouts.

Learning Paths (Collapsed)

Learning Progress (Collapsed)

Current-user training progress, certifications, and dashboard summaries.

Managed Uploads (Collapsed)

Managed multipart uploads for escalation recordings and comment attachments.

Marketplace Payout Settings (Collapsed)

Marketplace seller earnings destination and automatic-transfer preferences.

Marketplace Payout Settings Operations

Modals (Collapsed)

My Day (Collapsed)

The caller's own My Day plan and oversight snapshot: read-only primitives an agent can consult when the day plan or process exceptions matter.

Notification Templates (Collapsed)

Tenant notification-template validation and test delivery.

Notification Templates Operations

Notifications (Collapsed)

Permissions (Collapsed)

Effective user permissions, access scopes, and the permission definitions catalog.

Platform Feedback (Collapsed)

Bug, feature, and support feedback submitted to the platform team.

Platform Feedback Operations

Process Definition Tools (Collapsed)

Compatibility operations for finding and managing process definitions.

Process Definitions (Collapsed)

Process Instances (Collapsed)

Running instances of process definitions and their field values.

Process Instances Operations

Program Definitions (Collapsed)

Recurring service packages (programs) your agency offers.

Programs (Collapsed)

Compatibility operations for recurring service program definitions.

Service Categories (Collapsed)

Discoverable client-service categories and their tenant-scoped catalog details.

Shared Sections (Collapsed)

Reusable read-only sections embedded in entity layouts.

Signatures (Collapsed)

Tenant and user signatures available for ticket replies.

Signatures Operations

Sop Feedback (Collapsed)

Authenticated user feedback on visible standard operating procedures.

Sop Feedback Operations

Sop Outdated Flags (Collapsed)

User reports and administrative resolution of outdated SOP content.

Sop Ratings (Collapsed)

Caller-owned SOP ratings and tenant-safe aggregate rating statistics.

Sop Reviews (Collapsed)

Advisory reviewer queues and durable SOP review decisions.

Sop Tags (Collapsed)

Tenant-defined tags used to categorize and filter standard operating procedures.

Sop Views (Collapsed)

Caller-owned SOP viewing sessions and progress.

Task Audit (Collapsed)

Tenant task audit history clipped by task permissions, scope, privacy, and definition access.

Task Audit Operations

Task Auto Assignment (Collapsed)

Read-only task auto-assignment configuration.

Task Auto Assignment Operations

Tenant AI Settings (Collapsed)

Tenant-wide AI availability and monthly spend controls.

Tenant Context (Collapsed)

One bootstrap call returning the reference data (profiles, users, client and task statuses/tags, status scopes, programs, processes) needed to ground ids before acting.

Tenant Context Operations

Ticket Auto Assignment (Collapsed)

Effective ticket assignment routing and tenant configuration.

Ticket Blacklist Rules (Collapsed)

Rules that block or review matching inbound ticket messages.

Ticket Communications (Collapsed)

Permissioned ticket communication delivery history.

Ticket Correspondents (Collapsed)

Deliberately authorized additional recipients for a ticket thread.

Ticket Correspondents Operations

Ticket Intake Audit (Collapsed)

Ticket intake decisions and sanitized sender previews.

Ticket Intake Audit Operations

Ticket Messages (Collapsed)

Chronological ticket conversations, including external messages and internal notes.

Ticket Reply Templates (Collapsed)

Ticket Review Queue (Collapsed)

Ticket Routing Rules (Collapsed)

Ticket Settings (Collapsed)

Tenant-wide ticket AI, review, auto-close, and intake defaults.

Ticket Style Profile (Collapsed)

Tenant ticket writing-style guidance used by drafting tools.

Ticket Whitelist Rules (Collapsed)

Rules that allow matching inbound messages to bypass intake blocks.

Time Entries (Collapsed)

Training Assessments (Collapsed)

Redacted assessment runtime and server-graded learner attempts.

Training Certifications (Collapsed)

Issued training certificates visible through progress-reporting policy.

Training Certifications Operations

Training Enrollments (Collapsed)

Self-service learner enrollment and restart actions.

Training Lesson Blocks (Collapsed)

Training Lessons (Collapsed)

Training Manager Reviews (Collapsed)

Advisory manager-review queues and durable review decisions.

Training Modules (Collapsed)

Training Quiz Attempts (Collapsed)

Immutable, server-graded lesson quiz attempts.

Training Settings (Collapsed)

Tenant training behavior and completion settings.

Training Video Progress (Collapsed)

Caller-owned absolute video playback progress.

User Certifications (Collapsed)

Training certifications earned by tenant users.

User Certifications Operations

Users (Collapsed)

Your agency's members. Resolve assigned_to and created_by ids here.

View Folders (Collapsed)

Folders that organize saved views.

View Folders Operations

Views (Collapsed)

Saved dashboards and data presentations, including their scope and display configuration.

Webhook Deliveries (Collapsed)

MCP (Collapsed)

External AI via MCP — AgencyTitan hosts a Model Context Protocol server so an external model (Claude, ChatGPT, Gemini, Grok, Cursor, Claude Code, or any MCP-capable host) can work with your agency data. The host model thinks; AgencyTitan returns a router-selected internal tool pack plus instructions/knowledge, then executes allowlisted tools and public REST as the authenticated identity. Every call is tenant-pinned and runs with that identity's permissions. This is not AgencyTitan calling a tenant's external MCP server.

Server URL (Streamable HTTP):

https://api.agencytitan.com/v1/mcp

Authentication

OAuth and API keys are equal first-class paths for MCP. Use whichever your client supports:

Path Best for How
OAuth 2.0 Claude.ai, ChatGPT, and other hosts that discover OAuth Paste the MCP URL; the host runs discovery → authorize → token
API key (bearer) Cursor, Claude Code, VS Code, and clients that set request headers Authorization: Bearer at_live_… (or an OAuth access token)

Both resolve to { userId, tenantId } and work on every /v1 surface (REST and MCP). Create keys and manage sessions under Settings → System → API & MCP.

Connect to AgencyTitan's MCP server

Cursor

Add the following to your .cursor/mcp.json file (or Settings → MCP):

{
  "mcpServers": {
    "agency-titan": {
      "url": "https://api.agencytitan.com/v1/mcp",
      "headers": { "Authorization": "Bearer at_live_YOUR_KEY_HERE" }
    }
  }
}

Claude Code

claude mcp add --transport http agency-titan https://api.agencytitan.com/v1/mcp \
  --header "Authorization: Bearer at_live_YOUR_KEY_HERE"

ChatGPT / Claude.ai

Paste the MCP URL into the host's custom connector UI:

https://api.agencytitan.com/v1/mcp

Use OAuth as the connection mechanism. Unauthenticated requests return 401 with WWW-Authenticate pointing at protected-resource metadata. The host then:

  1. Fetches /.well-known/oauth-protected-resource/v1/mcp
  2. Fetches /.well-known/oauth-authorization-server
  3. Registers a public client via POST /oauth/register (Dynamic Client Registration)
  4. Opens AgencyTitan's authorize page (PKCE) so you pick which agency to grant
  5. Exchanges the code at POST /oauth/token and calls MCP with the access token

No API key paste is required for this path.

VS Code

Add the following to your .vscode/mcp.json file in your workspace:

{
  "servers": {
    "agency-titan": {
      "type": "http",
      "url": "https://api.agencytitan.com/v1/mcp",
      "headers": { "Authorization": "Bearer at_live_YOUR_KEY_HERE" }
    }
  }
}

Custom / Other

MCP is an open protocol supported by many clients. Use the server URL https://api.agencytitan.com/v1/mcp and OAuth when the client supports it. If your client does not support OAuth, pass an API key in the Authorization header as a Bearer token:

{
  "mcpServers": {
    "agency-titan": {
      "url": "https://api.agencytitan.com/v1/mcp",
      "headers": { "Authorization": "Bearer at_live_YOUR_KEY_HERE" }
    }
  }
}

Rate limits

MCP calls are limited to 240 requests per minute per agency, plus 120 requests per minute per subject (API key or OAuth client), separately from the REST budget. Exceeding either limit returns a JSON-RPC error with a Retry-After header.

Tools (Collapsed)

The AgencyTitan MCP server advertises a fixed namespaced set of meta-tools (agencytitan_*). Internal tools from the start pack are not listed individually in tools/list; call them through agencytitan_call_tools.

Typical flow

Every conversation must start with agencytitan_start. Do not begin with agencytitan_call_api.

agencytitan_start
        │
        ▼
agencytitan_call_tools  ←── execute tools from the returned pack (concurrent OK)
        │
        ▼
agencytitan_request_additional_tools  ←── when the pack is insufficient
        │
        ▼
agencytitan_call_tools  ←── call newly added tools

Optional helpers at any time after start:

  • agencytitan_get_tool_deep_guidance — extended authoring details for one pack tool
  • agencytitan_investigate — read-only nested investigation digest (prefer over paging fat REST lists)
  • agencytitan_call_apilast-resort public /v1 REST pass-through when pack tools cannot do the job

For agencytitan_call_api, never invent paths or query params. Look up the contract at https://www.agencytitan.com/docs/ or /v1/openapi.json.

If instructions/knowledge are insufficient, call agencytitan_start again with a refined prompt and the same conversation_id to refresh the pack.

agencytitan_start

Required first call for every conversation (new or continued). Send the user prompt (or your summary). Returns a router-selected internal tool pack, curated instructions/knowledge, identity, and conversation_id. Do normal work with agencytitan_call_tools after this.

Param Type Required Description
prompt string yes Natural-language request or summarized intent
client_model string yes External model id for logging (e.g. gpt-5.5, claude-sonnet-5)
conversation_id string no Prior conversation_id to continue the same session
client_provider string no Provider if known (openai, anthropic, google, …)
client_id string no Optional client UUID to ground the request
task_id string no Optional task UUID to ground the request
ticket_id string no Optional ticket UUID to ground the request
enable_rag boolean no When true (default), include tenant knowledge retrieval

agencytitan_call_tools

Execute one or more AgencyTitan internal tools from the pack returned by agencytitan_start. Supports concurrent calls. Only tools in the current session allowlist (plus control tools) are permitted.

Param Type Required Description
conversation_id string yes From agencytitan_start
calls array yes Non-empty list of { name, arguments? }

Each call item:

Field Type Required Description
name string yes Internal tool name from the start pack
arguments object no Arguments for that tool

agencytitan_request_additional_tools

Ask AgencyTitan to expand the selected tool pack when current tools are insufficient. Returns newly added tool schemas; then call them via agencytitan_call_tools.

Param Type Required Description
conversation_id string yes From agencytitan_start
reason string yes Why the current tools are insufficient and what you still need
missing_capabilities string[] no Short list of missing capabilities
suggested_tool_names string[] no Candidate internal tool names

agencytitan_get_tool_deep_guidance

Fetch extended authoring guidance for a specific internal tool from the current pack.

Param Type Required Description
conversation_id string yes From agencytitan_start
tool_name string yes Internal tool name

agencytitan_investigate

Run a read-only nested investigation against AgencyTitan data and return a concise digest.

Param Type Required Description
conversation_id string yes From agencytitan_start
prompt string yes What to investigate
focus_topics string[] no Optional focus topics

agencytitan_call_api

Last-resort pass-through to the AgencyTitan public REST API (/v1). Provide method, path, and optional query/body. Executes as the authenticated MCP user through the same facades as OpenAPI. Prefer agencytitan_call_tools / agencytitan_request_additional_tools / agencytitan_investigate first. Do not invent paths or params — use https://www.agencytitan.com/docs/ or /v1/openapi.json.

Param Type Required Description
method string yes GET, POST, PUT, PATCH, or DELETE
path string yes Public API path, e.g. /v1/clients or /v1/tickets/{id}
query object no Query string parameters
body object no JSON body for POST/PUT/PATCH
path_params object no Explicit path params if not embedded in path

Webhooks (Collapsed)

AgencyTitan can POST tenant events to your HTTPS endpoint when enabled events occur. Configure outbound webhooks from Settings -> System -> Webhooks with one global endpoint, per-event enable toggles, optional per-event override URLs, a signing secret, a ping test, and a delivery log with manual redelivery.

Every delivery uses this JSON envelope. The top-level id is unique per event and should be used for idempotency on your side:

{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "event_type": "client.created",
  "created_at": "2026-07-08T07:26:37.000Z",
  "tenant_id": "123e4567-e89b-12d3-a456-426614174001",
  "data": {
    "...": "event-specific fields"
  }
}

Each delivery is an HTTPS POST with these headers:

  • Content-Type: application/json
  • User-Agent: AgencyTitan-Webhooks/1.0
  • X-AT-Signature: t=<unix_seconds>,v1=<hex_hmac_sha256>
  • X-AT-Event: <event type>
  • X-AT-Delivery-Id: <delivery row id>
  • X-AT-Event-Id: <event envelope id>

The signature is computed as hex(HMAC_SHA256(secret, ${t}.${rawBody})), where the secret is the tenant webhook signing secret (whsec_...). Verify the raw request body before you parse JSON, reject stale timestamps (recommended: 5 minutes), and use a constant-time comparison.

import { createHmac, timingSafeEqual } from 'node:crypto';

export function verifyAgencyTitanWebhook({ rawBody, signatureHeader, secret, toleranceSeconds = 300 }) {
  const pairs = Object.fromEntries(
    signatureHeader.split(',').map((part) => {
      const [key, value] = part.split('=');
      return [key, value];
    }),
  );

  const timestamp = Number(pairs.t);
  const received = pairs.v1;
  if (!Number.isFinite(timestamp) || !received) return false;

  const ageSeconds = Math.abs(Math.floor(Date.now() / 1000) - timestamp);
  if (ageSeconds > toleranceSeconds) return false;

  const expected = createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  if (received.length !== expected.length) return false;

  return timingSafeEqual(
    Buffer.from(received, 'utf8'),
    Buffer.from(expected, 'utf8'),
  );
}
Generic verification flow:
1. Parse `X-AT-Signature` into `t` and `v1`.
2. Reject the request if `t` is older than your replay window.
3. Compute HMAC-SHA256 over `${t}.${rawBody}` with your signing secret.
4. Constant-time compare the computed digest to `v1`.
5. Only then trust the JSON payload.

Any 2xx response counts as delivered. Respond within 30 seconds; timeouts, network failures, and any non-2xx response are retried up to 8 total attempts with backoff after each failed delivery: 30 seconds, 2 minutes, 10 minutes, 30 minutes, 2 hours, 6 hours, and 12 hours. After 50 consecutive failures, AgencyTitan auto-disables the endpoint until it is saved as active again.

Available events

Category Event Description
client client_note.created Triggers when a new note is added to a client
client client_note.updated Triggers when a client note is modified
client client.bank_authorization_required Triggers when a saved bank (ACH) payment method needs client authorization before it can be charged (e.g. imported from a PSP without a usable mandate)
client client.created Triggers when a new client is created. Fires only for initial client creation; prefer client.updated for later tracked field changes.
client client.deleted Triggers when a client is deleted
client client.merged Triggers when a client is merged into a surviving client
client client.updated Triggers when a client is updated. Fires for tracked field changes after creation and carries _changed_fields; prefer client.created for initial creation.
client_program client_program.added Triggers when a client is subscribed to a program
client_program client_program.billing_changed Triggers when billing frequency, amount, or pricing override changes
client_program client_program.cancelled Triggers when a client manually cancels their program subscription. Cancellation is a churn event: recovery / win-back / retention follow-ups belong here as well as on client_program.suspended.
client_program client_program.completed Triggers when a program reaches its end date and completes
client_program client_program.paused Triggers when an active client program is paused by user
client_program client_program.resumed Triggers when a paused or suspended client program is resumed
client_program client_program.started Triggers when a program actually starts (status = active AND start date arrived)
client_program client_program.suspended Triggers when a client program is suspended due to payment failure
custom_object custom_object_record.created Triggers when a new custom object record is created
custom_object custom_object_record.deleted Triggers when a custom object record is deleted
custom_object custom_object_record.updated Triggers when a custom object record is updated
escalation escalation.comment_added Triggers when a comment is added to an escalation
escalation escalation.created Triggers when a new escalation is created. Fires only for initial escalation creation; prefer escalation.updated for later detail changes.
escalation escalation.reassigned Triggers when an escalation is assigned to a different user
escalation escalation.status_changed Triggers when an escalation transitions between statuses
escalation escalation.updated Triggers when escalation details change. Fires for detail changes after creation and carries _changed_fields; for a specific-field-changed intent, filter _changed_fields for that field, and prefer escalation.created for initial creation.
forms form.submission_ready Fires when a submission is finalized and ready to act on. Prefer form.submission for pre-review handling immediately on arrival.
forms form.submission_rejected Fires after a reviewer rejects a held submission.
manual manual.button Triggers when a button or action link is clicked by a user
sop sop.archived Triggers when an SOP is archived
sop sop.feedback_received Triggers when feedback is submitted on an SOP (ratings, comments) or when an SOP is flagged as outdated
sop sop.published Triggers when an SOP goes live (new or draft→published)
sop sop.updated Triggers when an SOP is updated (content, steps, or metadata)
task process.instance_completed Triggers when a task is completed (also fires task updated)
task process.instance_created Triggers when a new task is created. Fires only for initial task creation; prefer process.instance_updated for changes to an existing task.
task process.instance_deleted Triggers when a task is deleted
task process.instance_updated Triggers when any task field changes (status, assignee, stage, custom fields, etc.). Fires for task changes after creation and carries _changed_fields; prefer process.instance_created for initial task creation.
task process.merged Triggers when a task is merged into another
task process.stage_changed Triggers when a task moves to a new stage (also fires task updated)
ticket ticket.all_linked_processes_completed Triggers when a linked task completes and all remaining non-deleted, non-reference-only task links on the ticket point to completed tasks
ticket ticket.assigned Triggers when one or more agents are assigned to a ticket
ticket ticket.created Triggers when a new ticket is opened (inbound, portal, manual, or workflow). Fires on every ticket creation regardless of source; for message-driven intake (the first inbound message opening a ticket), prefer ticket.message_received with the is_new_ticket boolean filter.
ticket ticket.draft_review_approved Triggers when a pending draft review is approved and the message proceeds to send
ticket ticket.draft_review_rejected Triggers when a pending draft review is rejected or revisions are requested
ticket ticket.draft_review_requested Triggers when an outbound ticket draft is queued for manager review
ticket ticket.linked_process_attached Triggers when an existing task is linked to a ticket
ticket ticket.linked_process_created Triggers when a task is created and linked from a ticket
ticket ticket.merged Triggers when a ticket is merged into another
ticket ticket.message_received Triggers on an inbound customer message on a ticket. In a task (process) automation it runs once per live linked task of that process type. Fires for every inbound customer message, including the first message that opens a ticket; use the is_new_ticket boolean filter to limit it to first-message intake, and prefer ticket.created for creations from any source.
ticket ticket.message_sent Triggers on an outbound agent message on a ticket (including auto-send)
ticket ticket.moved Triggers when a ticket is moved to a different inbox
ticket ticket.priority_changed Triggers when a ticket priority is updated
ticket ticket.sla_at_risk Triggers when a ticket enters the at-risk SLA window (first response or resolution)
ticket ticket.sla_breached Triggers when a ticket breaches an SLA deadline (first response or resolution)
ticket ticket.spam_marked Triggers when a ticket (or the sender behind it) is marked as spam
ticket ticket.status_changed Triggers when a ticket transitions between statuses
ticket ticket.unassigned Triggers when agents are removed from a ticket (all or partial)
ticket ticket.updated Triggers when ticket fields change (subject, tags, inbox, client, etc.). Does not fire for pure status, priority, or assignment changes - use the dedicated triggers for those. Fires only for changes after creation and carries _changed_fields; prefer ticket.created for initial creation or the dedicated change trigger for status, priority, or assignment changes.
training training.assessment_failed Triggers when a user fails a training assessment
training training.assessment_passed Triggers when a user passes a training assessment
training training.assessment_submitted Triggers when a user submits a training assessment
training training.course_completed Triggers when a user completes a training course
training training.course_failed Triggers when a user fails a training course
user user.invited Triggers when a new user is invited. Fires at invitation time, before the user has accepted or completed setup; prefer user.setup_completed for automations that act on a ready account (training assignments, workspace provisioning).
user user.setup_completed Triggers when a user finishes the in-app user setup flow for an agency. The user's account is ready for in-app work from this point; prefer user.invited for pre-acceptance messaging.

MCP | AgencyTitan API Reference

# MCP

URL: https://www.agencytitan.com/docs/tag/mcp

**External AI via MCP** — AgencyTitan hosts a [Model Context Protocol](https://modelcontextprotocol.io) server so an external model (Claude, ChatGPT, Gemini, Grok, Cursor, Claude Code, or any MCP-capable host) can work with your agency data. The host model thinks; AgencyTitan returns a router-selected internal tool pack plus instructions/knowledge, then executes allowlisted tools and public REST as the authenticated identity. Every call is **tenant-pinned** and runs with that identity's permissions. This is not AgencyTitan calling a tenant's external MCP server.

Server URL (Streamable HTTP):

### `https://api.agencytitan.com/v1/mcp`

## Authentication

OAuth and API keys are **equal first-class paths** for MCP. Use whichever your client supports:

| Path | Best for | How |
|---|---|---|
| **OAuth 2.0** | Claude.ai, ChatGPT, and other hosts that discover OAuth | Paste the MCP URL; the host runs discovery → authorize → token |
| **API key (bearer)** | Cursor, Claude Code, VS Code, and clients that set request headers | `Authorization: Bearer at_live_…` (or an OAuth access token) |

Both resolve to `{ userId, tenantId }` and work on every `/v1` surface (REST and MCP). Create keys and manage sessions under **Settings → System → API & MCP**.

## Connect to AgencyTitan's MCP server

### Cursor

Add the following to your `.cursor/mcp.json` file (or Settings → MCP):

```json
{
  "mcpServers": {
    "agency-titan": {
      "url": "https://api.agencytitan.com/v1/mcp",
      "headers": { "Authorization": "Bearer at_live_YOUR_KEY_HERE" }
    }
  }
}
```

### Claude Code

```bash
claude mcp add --transport http agency-titan https://api.agencytitan.com/v1/mcp \
  --header "Authorization: Bearer at_live_YOUR_KEY_HERE"
```

### ChatGPT / Claude.ai

Paste the MCP URL into the host's custom connector UI:

```text
https://api.agencytitan.com/v1/mcp
```

Use **OAuth** as the connection mechanism. Unauthenticated requests return `401` with `WWW-Authenticate` pointing at protected-resource metadata. The host then:

1. Fetches `/.well-known/oauth-protected-resource/v1/mcp`
2. Fetches `/.well-known/oauth-authorization-server`
3. Registers a public client via `POST /oauth/register` (Dynamic Client Registration)
4. Opens AgencyTitan's authorize page (PKCE) so you pick which agency to grant
5. Exchanges the code at `POST /oauth/token` and calls MCP with the access token

No API key paste is required for this path.

### VS Code

Add the following to your `.vscode/mcp.json` file in your workspace:

```json
{
  "servers": {
    "agency-titan": {
      "type": "http",
      "url": "https://api.agencytitan.com/v1/mcp",
      "headers": { "Authorization": "Bearer at_live_YOUR_KEY_HERE" }
    }
  }
}
```

### Custom / Other

MCP is an open protocol supported by many clients. Use the server URL `https://api.agencytitan.com/v1/mcp` and OAuth when the client supports it. If your client does not support OAuth, pass an API key in the `Authorization` header as a Bearer token:

```json
{
  "mcpServers": {
    "agency-titan": {
      "url": "https://api.agencytitan.com/v1/mcp",
      "headers": { "Authorization": "Bearer at_live_YOUR_KEY_HERE" }
    }
  }
}
```

## Rate limits

MCP calls are limited to **240 requests per minute per agency**, plus **120 requests per minute per subject** (API key or OAuth client), separately from the REST budget. Exceeding either limit returns a JSON-RPC error with a `Retry-After` header.

## Operations

_This tag has no REST operations (guide / concept page)._

## Useful links

- Interactive page: https://www.agencytitan.com/docs/tag/mcp
- API reference home: https://www.agencytitan.com/docs/
- This page as Markdown: https://www.agencytitan.com/docs/tag/mcp.md
- Full API Markdown: https://api.agencytitan.com/v1/llms.txt