Skip to content
DeveloperLast updated: 2026-07-19

Scopable API: Getting Started Guide

Generate an API key from Settings > Developer, authenticate requests to api.scopable.io, and start reading and writing clients and contacts with cursor pagination, idempotent writes, and signed outbound webhooks.

Scopable Team6 min read

Prerequisites

  • A Scopable account with admin permissions
  • Access to Settings → Developer in Scopable

You don't need a Scopable-built app to work with your CRM data. The Scopable API is a versioned REST surface (/v1) authenticated by an API key, so any external system, script, or automation tool can read and write clients and contacts directly.

What the Scopable API does

Once you generate a key, external systems can:

  • Read clients and contacts: list with cursor-based pagination, or fetch a single record by id.
  • Create clients and contacts: writes are matched against your existing records or created fresh, using the same validation as an in-app create.
  • Update and archive records: PATCH updates an existing client or contact; DELETE archives it rather than hard-deleting it.
  • Receive outbound webhooks: get notified with HMAC-signed payloads whenever a client or contact changes, instead of polling.

v1 covers Clients and Contacts only. Opportunities, quotes, and bulk import aren't available yet.

Step 1: Generate an API key

  1. Log in to Scopable as an Admin.
  2. Go to Settings → Developer.
  3. Create a new API key and choose Live or Test mode.
  4. Select the scopes the key needs: clients:read, clients:write, contacts:read, contacts:write. Only grant write scopes to systems that actually need to create or update records.
  5. Copy the key immediately. It's shown once, in full, at creation. Scopable stores only a hashed, unrecoverable version, so a lost key means generating a new one, not recovering the old one.

Live keys (sk_live_...) act on real tenant data. Test keys (sk_test_...) are for building and debugging your integration safely.

Step 2: Authenticate your first request

Every request sends the key as a Bearer token in the Authorization header, against the api.scopable.io base URL:

curl -s https://api.scopable.io/v1/ping \
  -H "Authorization: Bearer $SCOPABLE_API_KEY"

A working key returns your tenant id, key id, granted scopes, and environment:

{
  "tenant_id": "...",
  "api_key_id": "...",
  "scopes": ["clients:read", "clients:write", "contacts:read", "contacts:write"],
  "environment": "live"
}

Store the key in an environment variable, not in source code. See Security and operations notes below.

Step 3: List and create records

List contacts:

curl -s "https://api.scopable.io/v1/contacts?limit=25" \
  -H "Authorization: Bearer $SCOPABLE_API_KEY"

Create a client:

curl -s https://api.scopable.io/v1/clients \
  -H "Authorization: Bearer $SCOPABLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: client-acme-001" \
  -d '{"name":"Acme IT","email":"[email protected]","external_id":"acme-001"}'

Create a contact under that client:

curl -s https://api.scopable.io/v1/contacts \
  -H "Authorization: Bearer $SCOPABLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: contact-ada-001" \
  -d '{"client_id":"<client-id>","name":"Ada Lovelace","email":"[email protected]","external_id":"ada-001"}'

The full resource set is GET/POST on /v1/clients and /v1/contacts, plus GET/PATCH/DELETE on /v1/clients/{id} and /v1/contacts/{id}. A live OpenAPI document, generated from the same schemas that validate every request, is always available with no key required at https://api.scopable.io/v1/openapi.json.

Pagination

Reads use cursor-based pagination, not page numbers:

  1. Send limit on the first request.
  2. The response includes next_cursor. Pass it back as cursor on the next request.
  3. next_cursor is null once you've reached the end.
{ "data": [ ], "next_cursor": "eyJvZmZzZXQiOjI1fQ" }

Idempotent writes

Retries are safe by design. Include either an Idempotency-Key header or an external_id field in the request body, and resending the same value updates the same record instead of creating a duplicate. That matters when a network timeout leaves you unsure whether the first request landed.

A POST never overwrites an existing match: if a contact with the same email already exists, the write links to it without changing it. Only PATCH mutates an existing record.

Error handling

Every error uses the same envelope:

{ "error": { "code": "not_found", "message": "...", "request_id": "..." } }

The same request_id is echoed in the X-Request-Id response header. Include it when contacting Scopable support about a failed call. Codes you'll see: unauthorized, forbidden, not_found, invalid_request, method_not_allowed, rate_limited, internal_error. A record that exists but belongs to another tenant returns not_found, never forbidden. The API never confirms that another tenant's data exists.

Exceeding your rate limit returns rate_limited with a Retry-After header telling you how long to wait before trying again.

Outbound webhooks

Configure a webhook endpoint under Settings → Developer → Webhooks to get pushed client and contact changes instead of polling. Payloads are HMAC-signed so you can verify they came from Scopable before trusting them.

Security and operations notes

Treat an API key like a password:

  • Store it in an environment variable or secrets manager, never in source control.
  • Grant only the scopes an integration actually needs. A read-only reporting tool doesn't need contacts:write.
  • Build and test against a Test key; switch to Live only when you're ready to touch real data.
  • Rotate a key immediately if it's ever exposed, and revoke unused keys from Settings → Developer.
  • Scopable never displays the full key again after creation. If you didn't copy it, generate a new one rather than trying to recover the old one.

Frequently Asked Questions

Do I need OAuth to use the API?

No. v1 authenticates with a Bearer API key. OAuth client-credentials support is planned for a future version but isn't required today.

What can I do with the API today?

Read, create, update, and archive clients and contacts, plus receive outbound webhooks for changes. Opportunities, quotes, and bulk import aren't part of v1.

What happens if two systems create the same contact?

Scopable matches on tenant and email. If a match exists, your write links to it instead of creating a duplicate. If not, a new contact is created using the same validation as an in-app create.

Can I see exactly what a call will do before I run it?

Yes. The OpenAPI document at /v1/openapi.json is generated from the same schemas that validate requests, so it can't drift from what the API actually accepts.

Does DELETE permanently remove a record?

No. DELETE archives the record rather than hard-deleting it, since contacts and clients are often mirrored from your PSA and a hard delete would fight that sync.

Where do I manage my keys?

Settings → Developer in Scopable: create, scope, and revoke API keys, and configure outbound webhooks, all in one place.