sally docs

HTTP API

Sally's web app runs on the same HTTP API you can call directly — the same routes, the same account, workspace, project, and key-scope permission checks. It's the right fit for scripts, sync jobs, and custom integrations. If you're wiring up an AI agent instead, the hosted MCP surface is usually the better path — it exposes the same capabilities as governed, self-describing tools.

The base URL is https://app.usesally.com/api. Every path below is relative to it.

Authentication

Every request resolves to a Sally Account, and a request can never reach further than the account, key, or connection behind it — the same permission model humans and agents share. There are three credential types:

  • Session token — what the web app uses. Sign in with POST /auth/login (email + password); if the account has a second factor, that returns a challenge you complete with POST /auth/login/2fa. A successful login returns a sessionToken. Send it as Authorization: Bearer <token> or X-Session-Token: <token>. Sessions last up to 30 days by default (admins can shorten this) and can be revoked at any time.
  • Personal API key — a long-lived key prefixed atpm_, created and revoked under Profile → security (GET/POST/DELETE /auth/api-keys). Send it as X-Api-Key: <key> or Authorization: Bearer <key>. Best for scripts and server-to-server integrations.
  • Hosted MCP key — a key prefixed sallymcp_, primarily for the hosted MCP endpoint but also accepted on the HTTP API. Send it as Authorization: Bearer <key>. An MCP key can be pinned to a single workspace; requests that name any other workspace are denied.
X-Api-Key: atpm_…

Keys carry scopes (for example read, write, admin, and finer agent scopes like tasks:write or crm:read). A request that needs a scope the key doesn't hold is rejected with 403 and a body of { "ok": false, "error": "…", "code": "KEY_SCOPE_DENIED", "requiredScope": "…" }. Account admins can require key expiry and restrict who may create keys — see Security.

A handful of routes need no credential: GET /health, the sign-in and invite/reset flows (/auth/login, /auth/login/2fa, /auth/accept-invite, /auth/invite-info, /auth/request-password-reset, /auth/reset-password), signup (/signup, /signup/confirm), public marketing embed/webhook endpoints, and served upload assets under /uploads.

Personal API keys and hosted MCP keys with scopes and expiry
Personal API keys and hosted MCP keys — each created with read / write / admin scopes, an optional expiry, and a workspace restriction.

Workspace context

Most resources belong to a brand (workspace). Name the brand on each request with a header:

X-Workspace-Id: <workspaceId>

or

X-Workspace-Slug: <workspaceSlug>

The same values also work as workspaceId / workspaceSlug query parameters. If you authenticate with a workspace-pinned MCP key, the pin decides the context (and conflicting values are refused). If you belong to exactly one brand and pass nothing, Sally uses it.

Main resources

The API mirrors the product surface. Collections are GET, creates are POST, updates are PATCH, and removals are DELETE.

Area Representative routes
Workspaces GET/POST /workspaces, GET/PATCH /workspaces/:id, …/members, …/roles
Projects GET/POST /projects, GET /projects/summary, GET/PATCH/DELETE /projects/:id, …/tasks, …/statuses, …/labels, …/members, …/dependencies
Board GET /board
Tasks GET/POST /tasks, GET/PATCH/DELETE /tasks/:id, …/comments, …/todos, …/labels, …/move, …/archive, …/dependencies
Handoff & resume POST /tasks/:id/handoff, GET /tasks/:id/resume-context, GET /projects/:id/runtime-context
Clients GET/POST /clients, GET/PATCH/DELETE /clients/:id
CRM GET/POST under /crm/organizations, /crm/people (POST /crm/people/:id/email), /crm/deals, /crm/activities, /crm/follow-ups
Relationships GET /relationships/people, GET /relationships/organizations
Marketing /marketing/contacts, /marketing/audiences, /marketing/senders, /marketing/messages, /marketing/campaigns, /marketing/forms, /marketing/content-gates, /marketing/deliveries, /marketing/approvals, /marketing/fields, /marketing/settings, /marketing/commerce/*
Memory GET/POST /memory, GET/PATCH/DELETE /memory/:id, POST /memory/intake, POST /memory/intake/execute
Opportunities GET /opportunities, GET /opportunities/:id/recommendation, POST /opportunities/:id/execute
Timesheets GET/POST /timesheets, GET /timesheets/report, GET /timesheets/users
Control Center GET/POST /agent-proposals, /agent-trust-policies, /approval-requests, /blockers, GET /blocked-tasks, GET /operational-events
Account & keys GET /auth/me, GET/PATCH /auth/profile, /auth/api-keys, /auth/mcp-keys, /notifications

Client and CRM Organization are the same user-facing record; the /clients routes stay for compatibility. For the full, self-describing catalog an agent can call — sally.context, task.handoff.create, memory.intake / memory.intake.execute, opportunity.recommend, proposal.create, and the rest — see Connect your agent.

Response shape

The response shape is not uniform — this is a deliberate note so you parse defensively rather than a promise of an envelope:

  • Writes typically return a small confirmation envelope, e.g. POST /projects{ "ok": true, "projectId": "…" }.
  • Reads typically return the data directly — a raw array or object, not wrapped in { ok, data }.
  • Errors return the matching HTTP status with { "ok": false, "error": "<message>" }. Scope failures add code and requiredScope as shown above.

Check the HTTP status first, then read the body for the shape that route returns.

Example

curl -s https://app.usesally.com/api/projects \
  -H "X-Api-Key: $SALLY_API_KEY" \
  -H "X-Workspace-Slug: my-brand"
curl -s -X POST https://app.usesally.com/api/tasks \
  -H "X-Api-Key: $SALLY_API_KEY" \
  -H "X-Workspace-Slug: my-brand" \
  -H "Content-Type: application/json" \
  -d '{"projectId": "…", "title": "Draft June newsletter", "priority": "P2"}'

Permissions and governance

HTTP calls pass the same checks as the web app — workspace roles, project roles, custom roles, and key scopes all apply. Risky, hard-to-reverse actions (marketing mass sends, publishing, deletions, exports) route through the same approval flow no matter how they're triggered, so an agent or script can't quietly do something a person would have to confirm. See Governance for the proposal, trust-policy, and approval model.

Roadmap

  • The ChatGPT connector ships a curated REST facade (/chatgpt/*) plus a generated OpenAPI schema as a private prototype; the general HTTP API above is the supported surface today.
  • External work-item sync (Jira, Linear, GitHub) is modeled but not yet wired; only Sally-native items are exercised today.