# For agents & developers — Stacks

> Scoped REST and JSON HTTP MCP access, verified local workflows, usage visibility and explicit client limitations.

Markdown version of https://stackscrm.com/developers — append `.md` to any page URL on this site (homepage: https://stackscrm.com/index.md). Index: https://stackscrm.com/llms.txt · Full site in one file: https://stackscrm.com/llms-full.txt

---

# For agents & developers

Connect your own software to Stacks. External integrations use the core API; built-in AI uses its own credits. Stacks-managed Agentic remains Coming Soon.

Use a separate scoped API key for each integration. Module, write, send and approval rules apply to every supported action. API usage is observed without commercial quota enforcement. Technical request and resource limits still apply.

## Start with a read

1. Open your workspace’s **Settings → API Keys**. Create a separate read-only key for this integration and select the modules it needs.
2. Copy the workspace URL shown there. Replace `YOUR_WORKSPACE` and `YOUR_KEY` in the illustrative request below. Keep keys out of shared prompts, source control and screenshots.
3. Read a small page, inspect the result, then retrieve usage. Enable write or send permission only for an intended workflow.

```
GET https://YOUR_WORKSPACE.stackscrm.com/api/v1/contacts?per_page=10
Authorization: Bearer YOUR_KEY

GET https://YOUR_WORKSPACE.stackscrm.com/api/v1/usage
Authorization: Bearer YOUR_KEY
```

These are templates, not an executable client configuration. Your workspace’s `/api/v1/openapi.json` and Settings → API Reference describe the REST schema. Authentication errors return 401, denied permissions 403, missing owned records 404, validation errors 422, conflicts 409 and technical throttles 429.

## MCP connection requirements

The MCP adapter supports stateless JSON HTTP with a custom Bearer header. Local protocol tests pass; hosted-client compatibility is unverified. Endpoint: `https://YOUR_WORKSPACE.stackscrm.com/mcp`.

POST an `initialize` request with a protocol-version date, then `tools/list`. Follow `nextCursor` until it is absent. Call tools with a JSON object for `arguments`. Supported selections are `2025-03-26` and `2025-06-18`; reject a negotiated version your client cannot use. No OAuth, GET event stream or resumable session is provided.

`get_api_usage` exposes observed units, operation weights, meter version and technical limits in both text and structured content. Commercial allowance and remaining are null in observe-only mode. Check `isError` and the business result even when HTTP returns 200. The tested client is a local PHP/cURL fixture; Claude, ChatGPT, Cursor and other hosted clients have not been verified.

## Read the outcome before the next action

Tasks return `is_completed` and `completed_at`. After `complete_task`, read the task again to confirm completion. Repeating completion preserves its timestamp. Deals return `status` (`open`, `won`, or `lost`), plus `won_at` and `lost_at`; pipeline stage alone does not establish the outcome.

Contact `tags` is always an array, including `[]` when empty. Contact context includes at most 20 contact-record activity entries, newest first by `created_at` then `id`. These entries contain identifiers and action names, with no message bodies or metadata. This is a contact-record history, not every event across the workspace.

`get_invoice` (REST `GET /api/v1/invoices/{id}`) returns headers and totals. Use `get_invoice_context` (REST `GET /api/v1/invoices/{id}/context`) for `line_items`, `payments`, `amount_paid` and `balance_due`. Invoice and contact context require both CRM and invoicing access; a missing permission rejects the entire combined request.

Discover inputs with `tools/list`: check `required`, valid choices in `enum`, and conditional inputs in `oneOf`. A follow-up needs exactly one of `contact_id` or `deal_id`. Dates use `YYYY-MM-DD`. Omit `assignee_id` when calling `assign_ticket` to unassign it.

MCP business errors have `isError: true` and an `error` object in both text and `structuredContent`. It contains `type`, `message`, `http_status`, `recovery` and a request reference. HTTP 200 is not proof of business success. Correct invalid inputs; reload after an `edit_conflict`. Inspect the existing record and activity reference before retrying an uncertain write.

```
{"isError":true,"structuredContent":{"error":{"type":"invalid_params","message":"arguments.tag: required.","http_status":400,"recovery":"Correct the named input using the tool schema, then retry.","request_id":"EXAMPLE_REFERENCE"}}}
```

`create_ticket` creates the optional first message as an agent-authored public-thread entry (`is_internal=false`). Creation sends no email, whether or not it is linked to a contact. This tool does not create private notes or send customer replies. Words such as “internal” in the subject do not change visibility.

`GET /api/v1/usage` and `get_api_usage` expose API units and technical limits. Hosted AI credits are separate. In observe-only mode, commercial allowance and remaining are `null`. Rejected operations and successful idempotent replays consume zero units. API Activity shows safe error categories, status codes and a timezone label; an `ok` operation does not confirm email delivery.

Timestamp fields such as `created_at`, `completed_at` and appointment `scheduled_at` use UTC in `YYYY-MM-DD HH:MM:SS` format. Treat them as UTC even though the string has no offset. Calendar fields such as `due_date` and `issued_date` are dates and must not be shifted. The workspace timezone controls CRM display; changing it preserves booked moments. An appointment's `timezone` is the guest preference, not the timezone of its stored timestamp.

## Available resource operations

| Resource | Core operations | Additional actions |
| --- | --- | --- |
| contacts | list, get, create, update, delete | tag, untag, request_review, send_email, send_sms |
| companies | list, get, create, update, delete | tag, untag |
| deals | list, get, create, update | win, lose |
| tasks | list, get, create, update | complete |
| invoices | list, get, create, update | record_payment, send_invoice |
| products | list, get, create, update, delete | — |
| tickets | list, get, create | assign, set_status, set_priority |
| users | list, get | — |
| pipelines | list, get | — |
| appointment_types | list, get | — |
| appointments | list, get | — |

The catalog lists supported operations, not your key’s permissions. Combined context reads require every included module. Appointments are read-only through these API resources; availability lookup does not book or reschedule a slot. Sending also requires the provider, current send rights and the account’s approval policy.

## Retries, outcomes and limits

Create/update of contacts, companies, tasks and products support an `idempotency_key`; REST also accepts `Idempotency-Key`. Use a random 16–128-character key per intended write. Reuse it after a timeout to retrieve the original result, including across REST and MCP. Different input with the same key conflicts. Updates can use `if_match` with the returned `_version`; conditional custom-field updates are not supported.

Other actions have their own recovery rules. A pending approval is not a sent message, provider acceptance is not delivery, and an unavailable paid booking can require staff resolution. Inspect the existing result before resubmitting an uncertain external send or payment.

REST and MCP share 120 requests per minute per key, four concurrent requests per key and eight per workspace. JSON bodies are limited to 1 MiB, lists to 100 records per page and MCP discovery to 32 tools per page. Large jobs should use bounded pages and stop when permissions or limits reject a request.

## Five local workflow examples

The repository’s automated workflow fixture is reproducible in isolated Docker and uses synthetic data. It demonstrates lead/company/deal/task creation, bounded relationship context, invoice ledger/status, calendar record inspection, and an internal follow-up that sends no message. Payment callbacks and booking conflicts have separate synthetic-provider regressions. These results do not establish provider-sandbox or hosted-client compatibility.

[Read the examples and their boundaries](https://stackscrm.com/developers/workflows) · [Built-in AI credits](https://stackscrm.com/features/turn-on-ai) · [Export coverage](https://stackscrm.com/features/export-contacts)

Connect your own supported email, SMS and payment providers. Saving credentials does not verify a successful send or payment.

Private integrations use their own scoped keys. A public multi-customer application, hosted agent runtime or distribution program is not implied by this API. [Start a Free workspace](https://stackscrm.com/register).
