OSTRATA · Developer documentation

API & MCP reference

Integrate with OSTRATA using the REST API (osk_ keys) or connect AI clients via MCP (omk_ keys). REST base URL: https://ostrata.io/api/v1

Remote MCP server URL: https://ostrata.io/api/mcp

Workspace admins create keys in Setup → API. You do not need a user account to read this documentation.

Introduction

The OSTRATA REST API lets external systems read and write workspace data: custom objects, fields, and records. It mirrors the same permission model as workspace members — each integration key is bound to a permission profile that controls what the key can do.

The API is versioned under /api/v1. Breaking changes will ship under a new version prefix; non-breaking additions stay on v1.

Authentication

Every request must include a workspace integration API key in the Authorization header. Keys are created by a workspace admin in Setup → API and are shown only once at creation.

Revoked keys are rejected immediately. Keys do not expire automatically — rotate by creating a new key and revoking the old one.

  • Keys start with the prefix osk_.
  • Never commit keys to source control or expose them in client-side apps.
  • Assign the narrowest permission profile that satisfies the integration.
Header
Authorization: Bearer osk_YOUR_SECRET_KEY

Getting started

You need an API key from a workspace administrator. If you are building an integration for a customer workspace, ask their admin to create a dedicated permission profile (Setup → Permissions) and an integration key (Setup → API) with that profile.

Replace the base URL below with your OSTRATA deployment origin (same host you use in the browser).

List contacts
curl -s -H "Authorization: Bearer osk_YOUR_KEY" \
  "https://ostrata.io/api/v1/objects/contacts/records?limit=5"
Create a contact
curl -s -X POST \
  -H "Authorization: Bearer osk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Jane Doe","fields":{"email":"jane@example.com"}}' \
  "https://ostrata.io/api/v1/objects/contacts/records"

Requests and responses

Request and response bodies are JSON. Use Content-Type: application/json on POST and PATCH requests with a body.

Successful responses use a data envelope. Errors use an error object with code and message.

  • unauthorized (401) — missing or invalid API key
  • forbidden (403) — key valid but action not allowed by profile
  • not_found (404) — object, field, or record not found
  • bad_request (400) — validation failed
  • conflict (409) — duplicate name or API name
Success
{
  "data": { "record": { "id": "…", "name": "Jane Doe", "fields": {} } }
}
Error
{
  "error": {
    "code": "forbidden",
    "message": "This API key does not have permission for this action."
  }
}

Permissions

Authorization is enforced from the permission profile attached to the API key — not from the admin who created the key. Create narrow profiles for integrations (e.g. “Zapier — contacts sync”) with only the object, field, and record rights required.

Record responses omit field values the profile cannot read. Create and update requests ignore fields the profile cannot edit.

  • object.create / object.update / object.delete — schema-level object changes
  • field.create / field.update / field.delete — field definitions on objects
  • Per-object record view / create / update / delete — configured in the profile matrix
  • Per-field read / edit — optional overrides per object

Objects and fields

Objects are addressed by apiName (e.g. contacts, deals, projects). Fields use apiName within their parent object.

Standard CRM objects ship with every workspace. Custom objects are created via the API or Setup UI.

Records

Records expose a name plus a fields object (maps to stored custom data). Lookup and address fields use the same key conventions as the OSTRATA UI.

List records supports limit (1–100), offset (0–10,000), and q for text search. Responses include count, offset, limit, and hasMore for pagination.

For large migrations, use bulk import jobs under /import/jobs instead of creating records one at a time.

Bulk import

Bulk import mirrors Setup → Data import: create a job, map columns, validate every row, then run. Requires a workspace admin permission profile on the API key.

Same limits apply: 10 MB files, 10,000 data rows, one primary object per job, optional one embedded related object. Validation is all-or-nothing — no partial writes.

Workflow: POST /import/jobs → PATCH mappings (optional) → POST …/validate → POST …/run. Jobs run in the background; poll GET …/jobs/:jobId for status.

  • JSON body — supply headers and rows directly (max 10,000 rows)
  • Multipart — upload CSV or Excel (.csv, .xlsx, .xls) with primaryObjectApiName form field
  • Standard and custom objects — primaryObjectApiName matches object API names
  • embedded — create/update related records from the same rows and link via a lookup field
  • duplicateMode — create, skip, or update (Contacts match email/phone; name-based objects match normalized name)
  • Auto-number fields are system-generated; computed quote totals cannot be imported
  • Events sync to Google Calendar when the API key actor has calendar connected
Create and validate
curl -s -X POST \
  -H "Authorization: Bearer osk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"fileName":"contacts.csv","headers":["email"],"rows":[{"email":"ada@example.com","first_name":"Ada","last_name":"Lovelace"}],"primaryObjectApiName":"contacts","duplicateMode":"update"}' \
  "https://ostrata.io/api/v1/import/jobs"

curl -s -X POST \
  -H "Authorization: Bearer osk_YOUR_KEY" \
  "https://ostrata.io/api/v1/import/jobs/JOB_ID/validate"
Run import
curl -s -X POST \
  -H "Authorization: Bearer osk_YOUR_KEY" \
  "https://ostrata.io/api/v1/import/jobs/JOB_ID/run"

Inbound webhooks

Workspace admins create inbound endpoints in Setup → Webhooks. External systems POST JSON to receive CRM record creates or updates.

Authenticate with Authorization: Bearer whk_… (token shown once when the endpoint is created).

Field mapping in Setup defines how JSON paths map to CRM fields. Action can be create, update, or upsert (match on a field such as email).

  • 201 — record created; 200 — record updated
  • Response body: { record_id, action: "created" | "updated" }
  • Permission profile on the endpoint caps field-level write access
  • Outbound record events are configured separately in Setup → Webhooks (not on /api/v1)
POST inbound payload
curl -s -X POST \
  -H "Authorization: Bearer whk_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Ada Lovelace","email":"ada@example.com"}' \
  "https://ostrata.io/api/webhooks/inbound/WEBHOOK_ID"

Overview

OSTRATA exposes a remote MCP server so Claude, Cursor, and other MCP clients can search and update CRM data on your behalf.

Each member creates their own connection from Personal space → MCP. Keys use the omk_ prefix and inherit the member's permission profile — the same boundaries as Operations.

Authentication

Send Authorization: Bearer omk_YOUR_KEY on every MCP request when using Claude Code or Cursor.

Claude Desktop and Claude web use OAuth instead: paste OAuth Client ID and Secret in Advanced settings, then sign in when Claude prompts you. Claude sends OAuth access tokens as Bearer on MCP calls.

Member MCP keys (omk_) work only on /api/mcp. Admin REST integration keys (osk_) work only on /api/v1 — they are separate products.

Claude (custom connector)

In Claude Desktop or Claude web: Settings → Connectors → Add custom connector. Paste the connector name, server URL, OAuth Client ID, and OAuth Client Secret from Personal space → MCP.

When you connect, Claude opens a browser so you can sign in to OSTRATA. That login ties the session to your workspace member — the same user who created the connection.

Claude Add custom connector dialog with Name, Remote MCP server URL, and OAuth fields in Advanced settings
Example
Name: OSTRATA
Remote MCP server URL: https://ostrata.io/api/mcp
OAuth Client ID: omc_…
OAuth Client Secret: ocs_…

Claude Code

Native HTTP transport — no proxy required.

claude mcp add --transport http ostrata https://ostrata.io/api/mcp \
  --header "Authorization: Bearer omk_YOUR_KEY"

Cursor

Add under Cursor Settings → MCP.

{
  "mcpServers": {
    "ostrata": {
      "url": "https://ostrata.io/api/mcp",
      "headers": {
        "Authorization": "Bearer omk_YOUR_KEY"
      }
    }
  }
}

Tools

Call list_objects first to discover object apiNames and fields. search_records supports query (text search), structured filters like Pilot, and pagination via offset + limit (up to 50 per page).

  • list_objects — List visible CRM object types and readable fields.
  • describe_object — Get one object schema by apiName.
  • search_records — Search records with query, filters, and offset/limit pagination.
  • get_record — Read one record.
  • create_record — Create a record (created_by = connection owner).
  • update_record — Update record fields.
  • delete_record — Delete a record.
  • list_related_records — List child records linked via lookups.
  • create_object — Create a custom object.
  • update_object — Update object metadata.
  • delete_object — Delete a custom object.
  • add_field — Add a field to an object.
  • update_field — Update a field definition.
  • remove_field — Remove a field.
  • list_import_jobs — List bulk import jobs (admin profile).
  • create_import_job — Create import job draft.
  • get_import_job — Get import job detail.
  • validate_import_job — Validate import mappings.
  • run_import_job — Execute import job.
  • list_attachments — List files on a record.
  • upload_attachment — Upload base64 file to a record.
  • delete_attachment — Delete an attachment.
  • draft_email_to_contact — AI draft email to a CRM contact.
  • send_email — Send email from connection owner's Gmail.

Endpoints

All paths below are relative to /api/v1. Path segments in italics (e.g. :objectApiName) are replaced with real values.

GET/api/v1/objects

Returns all object types the API key may view, including standard CRM objects (Contacts, Deals, etc.) and custom objects. Each object includes readable field metadata.

Permission: Object view (via permission profile)Since 2026-06-03
Response example
{
  "data": {
    "objects": [
      {
        "apiName": "contacts",
        "name": "Contacts",
        "isDefault": true,
        "fields": [
          {
            "apiName": "email",
            "name": "Email",
            "type": "email",
            "required": false
          }
        ]
      }
    ]
  }
}
Example
curl -H "Authorization: Bearer osk_YOUR_KEY" \
  "https://ostrata.io/api/v1/objects"
POST/api/v1/objects

Creates a new custom object type in the workspace. Requires object.create on the key's permission profile.

Permission: object.createSince 2026-06-03
Request body example
{
  "name": "Projects",
  "apiName": "projects",
  "attachmentsEnabled": true
}
Response example
{
  "data": {
    "object": {
      "apiName": "projects",
      "name": "Projects",
      "isDefault": false,
      "fields": []
    }
  }
}
Example
curl -X POST -H "Authorization: Bearer osk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Projects","apiName":"projects"}' \
  "https://ostrata.io/api/v1/objects"
GET/api/v1/objects/:objectApiName

Fetches a single object by its API name, including field definitions the key may read.

Permission: Object viewSince 2026-06-03

Path parameters

NameDescription
objectApiNameStable API name (e.g. contacts, deals).
PATCH/api/v1/objects/:objectApiName

Updates the display name and/or API name of an object.

Permission: object.updateSince 2026-06-03

Path parameters

NameDescription
objectApiNameCurrent API name of the object.
Request body example
{
  "name": "Customers",
  "apiName": "customers"
}
DELETE/api/v1/objects/:objectApiName

Deletes a custom object and its schema. Default CRM objects cannot be deleted.

Permission: object.deleteSince 2026-06-03

Path parameters

NameDescription
objectApiNameAPI name of the custom object.
Response example
{
  "data": {
    "deleted": true
  }
}
GET/api/v1/objects/:objectApiName/fields

Lists field definitions on an object that the API key may read.

Permission: Object viewSince 2026-06-03

Path parameters

NameDescription
objectApiNameParent object API name.
POST/api/v1/objects/:objectApiName/fields

Adds a new field to an object schema.

Permission: field.createSince 2026-06-03

Path parameters

NameDescription
objectApiNameParent object API name.
Request body example
{
  "name": "Industry",
  "apiName": "industry",
  "type": "select",
  "required": false,
  "selectOptions": [
    {
      "label": "SaaS",
      "apiName": "saas"
    }
  ]
}
GET/api/v1/objects/:objectApiName/fields/:fieldApiName

Returns one field definition.

Permission: Object view + field readSince 2026-06-03

Path parameters

NameDescription
objectApiNameParent object API name.
fieldApiNameField API name.
PATCH/api/v1/objects/:objectApiName/fields/:fieldApiName

Updates field label, API name, type, select options, or currency code.

Permission: field.updateSince 2026-06-03

Path parameters

NameDescription
objectApiNameParent object API name.
fieldApiNameField API name.
Request body example
{
  "name": "Industry segment"
}
DELETE/api/v1/objects/:objectApiName/fields/:fieldApiName

Removes a user-defined field from an object.

Permission: field.deleteSince 2026-06-03

Path parameters

NameDescription
objectApiNameParent object API name.
fieldApiNameField API name.
GET/api/v1/objects/:objectApiName/records

Lists records for an object. Field values in responses are filtered to what the key may read.

Permission: Record view on objectSince 2026-06-03

Path parameters

NameDescription
objectApiNameObject API name (e.g. contacts).

Query parameters

NameDescription
limitMax records to return (1–100, default 25).
offsetNumber of matching records to skip for pagination (0–10,000, default 0).
qOptional text search across record name and fields.
Response example
{
  "data": {
    "records": [
      {
        "id": "uuid",
        "name": "Jane Doe",
        "fields": {
          "email": "jane@example.com"
        }
      }
    ],
    "count": 1,
    "offset": 0,
    "limit": 25,
    "hasMore": false
  }
}
Example
curl -H "Authorization: Bearer osk_YOUR_KEY" \
  "https://ostrata.io/api/v1/objects/contacts/records?limit=20&offset=0"
POST/api/v1/objects/:objectApiName/records

Creates a record. Writable fields are limited by the permission profile.

Permission: Record create on objectSince 2026-06-03

Path parameters

NameDescription
objectApiNameObject API name.
Request body example
{
  "name": "Jane Doe",
  "fields": {
    "email": "jane@example.com",
    "status": "active"
  }
}
Example
curl -X POST -H "Authorization: Bearer osk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Jane Doe","fields":{"email":"jane@example.com"}}' \
  "https://ostrata.io/api/v1/objects/contacts/records"
GET/api/v1/objects/:objectApiName/records/:recordId

Returns a single record by UUID.

Permission: Record view on objectSince 2026-06-03

Path parameters

NameDescription
objectApiNameObject API name.
recordIdRecord UUID.
PATCH/api/v1/objects/:objectApiName/records/:recordId

Updates record name and/or field values.

Permission: Record update on object + field editSince 2026-06-03

Path parameters

NameDescription
objectApiNameObject API name.
recordIdRecord UUID.
Request body example
{
  "fields": {
    "status": "customer"
  }
}
DELETE/api/v1/objects/:objectApiName/records/:recordId

Permanently deletes a record.

Permission: Record delete on objectSince 2026-06-03

Path parameters

NameDescription
objectApiNameObject API name.
recordIdRecord UUID.
GET/api/v1/import/jobs

Lists bulk import jobs for the workspace. Requires a workspace admin permission profile on the API key.

Permission: Workspace admin profile on API keySince 2026-06-18

Query parameters

NameDescription
limitMax jobs to return (1–100, default 25).
Response example
{
  "data": {
    "jobs": [
      {
        "id": "…",
        "fileName": "contacts.csv",
        "status": "succeeded",
        "rowCount": 120,
        "primaryObject": {
          "apiName": "contacts",
          "name": "Contacts"
        }
      }
    ]
  }
}
Example
curl -H "Authorization: Bearer osk_YOUR_KEY" \
  "https://ostrata.io/api/v1/import/jobs?limit=25"
POST/api/v1/import/jobs

Creates a bulk import job. Send JSON with headers and rows, or multipart/form-data with a file field plus primaryObjectApiName. Discards other draft sessions for the key actor. Same validation and duplicate rules as Setup → Data import.

Permission: Workspace admin profile on API keySince 2026-06-18
Request body example
{
  "fileName": "contacts.csv",
  "headers": [
    "first_name",
    "last_name",
    "email"
  ],
  "rows": [
    {
      "first_name": "Ada",
      "last_name": "Lovelace",
      "email": "ada@example.com"
    }
  ],
  "primaryObjectApiName": "contacts",
  "sourceType": "external_crm",
  "duplicateMode": "update",
  "primaryMappings": [
    {
      "fileColumn": "first_name",
      "fieldApiName": "first_name"
    },
    {
      "fileColumn": "email",
      "fieldApiName": "email"
    }
  ],
  "embedded": {
    "enabled": true,
    "objectApiName": "companies",
    "linkFieldApiName": "company",
    "matchKeyColumn": "company_name",
    "mappings": [
      {
        "fileColumn": "company_name",
        "fieldApiName": "name"
      }
    ]
  }
}
Response example
{
  "data": {
    "job": {
      "id": "…",
      "status": "draft",
      "primaryObject": {
        "apiName": "contacts",
        "name": "Contacts"
      }
    }
  }
}
Example
curl -X POST -H "Authorization: Bearer osk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"fileName":"contacts.csv","headers":["first_name","email"],"rows":[{"first_name":"Ada","email":"ada@example.com"}],"primaryObjectApiName":"contacts"}' \
  "https://ostrata.io/api/v1/import/jobs"
GET/api/v1/import/jobs/:jobId

Returns one import job with config, validation result, and result summary when finished.

Permission: Workspace admin profile on API keySince 2026-06-18

Path parameters

NameDescription
jobIdImport job UUID.
PATCH/api/v1/import/jobs/:jobId

Updates source type, primary object, duplicate mode, column mappings, or embedded object config.

Permission: Workspace admin profile on API keySince 2026-06-18

Path parameters

NameDescription
jobIdImport job UUID.
Request body example
{
  "duplicateMode": "update",
  "primaryMappings": [
    {
      "fileColumn": "email",
      "fieldApiName": "email"
    }
  ]
}
DELETE/api/v1/import/jobs/:jobId

Removes an import job from history. Cannot delete running jobs.

Permission: Workspace admin profile on API keySince 2026-06-18

Path parameters

NameDescription
jobIdImport job UUID.
Response example
{
  "data": {
    "deleted": true
  }
}
POST/api/v1/import/jobs/:jobId/validate

Runs an all-or-nothing dry-run on every row. On success the job status becomes validated and can be run.

Permission: Workspace admin profile on API keySince 2026-06-18

Path parameters

NameDescription
jobIdImport job UUID.
POST/api/v1/import/jobs/:jobId/run

Executes a validated import job in the background. Returns 202 Accepted when the platform supports deferred work; otherwise waits and returns the finished job.

Permission: Workspace admin profile on API keySince 2026-06-18

Path parameters

NameDescription
jobIdImport job UUID.
Response example
{
  "data": {
    "jobId": "…",
    "status": "running"
  }
}
Example
curl -X POST -H "Authorization: Bearer osk_YOUR_KEY" \
  "https://ostrata.io/api/v1/import/jobs/JOB_UUID/run"

Request access

Tell us about your team. We'll reach out when your workspace is ready.

API Documentation — OSTRATA AI CRM