RoboCRM API

REST · JSON · v1 — base URL https://crm.simulatefin.com/api/v1 · OpenAPI 3 (JSON)

Authentication

Create a key in the CRM under Integrations → API keys (organization admin, Pro / Enterprise plans) and choose its scopes. The key is shown once. Send it on every request as Authorization: Bearer rcrm_… or X-API-Key: rcrm_…. Requests run inside the key's organization; records it creates are owned by the key's creator unless you pass owner_email. Rate limit: 300 requests per minute per key (HTTP 429 with Retry-After). Bodies are JSON (Content-Type: application/json); form posts are also accepted.
curl "https://crm.simulatefin.com/api/v1/me" -H "Authorization: Bearer rcrm_…"
ScopeAllows
clients:readread clients
clients:writecreate / update / delete clients
deals:readread deals and pipelines
deals:writecreate / update deals
tasks:readread tasks
tasks:writecreate / update tasks
activities:writeadd timeline entries
catalog:readread the price list
calls:writesend PBX call events
calls:readcall log and caller ID lookup

Endpoints

GET/me

Organization, key name and scopes of the calling key.
curl -X GET "https://crm.simulatefin.com/api/v1/me" \
  -H "Authorization: Bearer rcrm_…"

GET/clientsclients:read

List clients. Filters: q, email, phone, status, updated_since (ISO date), page, per_page (max 100).
curl -X GET "https://crm.simulatefin.com/api/v1/clients" \
  -H "Authorization: Bearer rcrm_…"

POST/clientsclients:write

Create a client. Add ?upsert=email or ?upsert=phone to update the existing client instead of creating a duplicate. Fires client.created.
curl -X POST "https://crm.simulatefin.com/api/v1/clients" \
  -H "Authorization: Bearer rcrm_…" \
  -H "Content-Type: application/json" \
  -d '{"name":"Dana Levi","email":"dana@levi-tech.co.il","phone":"052-1234567","company":"Levi Tech","status":"lead","source":"website","owner_email":"agent@company.co.il","tags":["VIP"],"custom_fields":{"industry":"Software"}}'

GET/clients/{id}clients:read

One client with tags and custom fields.
curl -X GET "https://crm.simulatefin.com/api/v1/clients/42" \
  -H "Authorization: Bearer rcrm_…"

PATCH/clients/{id}clients:write

Update any of the fields above. Status changes fire client.status_changed.
curl -X PATCH "https://crm.simulatefin.com/api/v1/clients/42" \
  -H "Authorization: Bearer rcrm_…" \
  -H "Content-Type: application/json" \
  -d '{"status":"customer"}'

DELETE/clients/{id}clients:write

Move the client to the trash (soft delete).
curl -X DELETE "https://crm.simulatefin.com/api/v1/clients/42" \
  -H "Authorization: Bearer rcrm_…"

GET/dealsdeals:read

List deals. Filters: client_id, pipeline_id, stage, updated_since.
curl -X GET "https://crm.simulatefin.com/api/v1/deals" \
  -H "Authorization: Bearer rcrm_…"

POST/dealsdeals:write

Create a deal. stage is a stage slug from GET /pipelines (default: first open stage of the default pipeline).
curl -X POST "https://crm.simulatefin.com/api/v1/deals" \
  -H "Authorization: Bearer rcrm_…" \
  -H "Content-Type: application/json" \
  -d '{"client_id":42,"title":"CRM rollout","amount":20000,"stage":"proposal","close_date":"2026-12-31"}'

GET/deals/{id}deals:read

One deal.
curl -X GET "https://crm.simulatefin.com/api/v1/deals/42" \
  -H "Authorization: Bearer rcrm_…"

PATCH/deals/{id}deals:write

Update title, amount, close_date, owner_email, pipeline_id, stage. Moving the stage fires deal.stage_changed.
curl -X PATCH "https://crm.simulatefin.com/api/v1/deals/42" \
  -H "Authorization: Bearer rcrm_…" \
  -H "Content-Type: application/json" \
  -d '{"stage":"won"}'

GET/pipelinesdeals:read

Pipelines with their stages (slug, name, kind open|won|lost, probability).
curl -X GET "https://crm.simulatefin.com/api/v1/pipelines" \
  -H "Authorization: Bearer rcrm_…"

GET/taskstasks:read

List tasks. Filters: client_id, status.
curl -X GET "https://crm.simulatefin.com/api/v1/tasks" \
  -H "Authorization: Bearer rcrm_…"

POST/taskstasks:write

Create a task.
curl -X POST "https://crm.simulatefin.com/api/v1/tasks" \
  -H "Authorization: Bearer rcrm_…" \
  -H "Content-Type: application/json" \
  -d '{"title":"Send contract","client_id":42,"due_date":"2026-10-20","priority":"high","assignee_email":"agent@company.co.il"}'

PATCH/tasks/{id}tasks:write

Update a task. status=done fires task.completed.
curl -X PATCH "https://crm.simulatefin.com/api/v1/tasks/42" \
  -H "Authorization: Bearer rcrm_…" \
  -H "Content-Type: application/json" \
  -d '{"status":"done"}'

POST/activitiesactivities:write

Add a timeline entry to a client: note, call, meeting, email, whatsapp or sms.
curl -X POST "https://crm.simulatefin.com/api/v1/activities" \
  -H "Authorization: Bearer rcrm_…" \
  -H "Content-Type: application/json" \
  -d '{"client_id":42,"type":"sms","body":"Reminder sent","occurred_at":"2026-10-08T10:00:00+03:00"}'

GET/productscatalog:read

Active products of the price list.
curl -X GET "https://crm.simulatefin.com/api/v1/products" \
  -H "Authorization: Bearer rcrm_…"

POST/callscalls:write

PBX call event (see "Connecting a PBX"). Idempotent per call_id: send ringing, answered and completed / missed for the same call_id.
curl -X POST "https://crm.simulatefin.com/api/v1/calls" \
  -H "Authorization: Bearer rcrm_…" \
  -H "Content-Type: application/json" \
  -d '{"call_id":"pbx-1700000000.123","status":"completed","direction":"inbound","from":"052-1234567","to":"03-5551234","extension":"201","started_at":"2026-10-08T10:00:00+03:00","duration":184,"recording_url":"https://pbx.example.com/rec/123.mp3"}'

GET/callscalls:read

Call log. Filters: client_id, status, since.
curl -X GET "https://crm.simulatefin.com/api/v1/calls" \
  -H "Authorization: Bearer rcrm_…"

GET/lookupcalls:read

Caller ID / screen pop: ?phone=0521234567 returns the client, its owner, the owner's extension and open deals.
curl -X GET "https://crm.simulatefin.com/api/v1/lookup?phone=0521234567" \
  -H "Authorization: Bearer rcrm_…"
List responses: {"data": [...], "meta": {"page": 1, "per_page": 25, "total": 130}}. Single objects: {"data": {...}}. Dates are YYYY-MM-DD HH:MM:SS in the organization's time zone.

Webhooks

Subscribe a URL under Integrations → Webhooks. Events: client.created, client.updated, client.status_changed, lead.web_form, deal.created, deal.stage_changed, task.created, task.completed, meeting.booked, email.received, quote.sent, quote.accepted, quote.rejected, invoice.paid, call.ringing, call.completed, call.missed, plus ping from the Test button. Each delivery is an HTTP POST with a JSON body:
{
    "id": "6f1c\u2026-uuid",
    "event": "client.created",
    "created_at": "2026-10-08T10:00:00+03:00",
    "organization_id": 1,
    "data": {
        "entity_type": "client",
        "object": {
            "id": 42,
            "name": "Dana Levi",
            "status": "lead"
        },
        "previous": null
    }
}

Headers: X-RoboCRM-Event, X-RoboCRM-Delivery (unique id — use it to ignore duplicates), X-RoboCRM-Timestamp, X-RoboCRM-Signature: t=<timestamp>,v1=<hex> where v1 = HMAC-SHA256(secret, timestamp + "." + raw_body). Reject requests older than 5 minutes. Answer with any 2xx within 10 seconds; otherwise the delivery is retried after 1 min, 5 min, 30 min, 2 h and 6 h. After 30 failures in a row the webhook is paused and the admins are notified.

// PHP
$body = file_get_contents("php://input");
preg_match("/t=(\d+),v1=([a-f0-9]+)/", $_SERVER["HTTP_X_ROBOCRM_SIGNATURE"] ?? "", $m);
$ok = $m && abs(time() - (int)$m[1]) < 300
   && hash_equals(hash_hmac("sha256", $m[1] . "." . $body, $secret), $m[2]);

// Node.js
const [, t, v1] = req.headers["x-robocrm-signature"].match(/t=(\d+),v1=([a-f0-9]+)/);
const ok = crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(
  crypto.createHmac("sha256", secret).update(t + "." + rawBody).digest("hex")));

Connecting a PBX

  1. In Integrations → Telephony turn telephony on, set the country code and give every agent their PBX extension.
  2. Create an API key with the scopes calls:write and calls:read for the PBX.
  3. Call events — on every state change the PBX sends POST /calls with the same call_id: ringing (the agent sees a pop-up with the caller's card), answered, and finally completed, missed, busy, failed or voicemail. Numbers may be local (052-1234567) or international (+972521234567). The CRM matches the client by phone, maps the agent by extension (or agent_email), logs the call on the client's timeline with duration and recording link, creates a lead for unknown callers and a "call back" task for missed calls (both optional), and fires call.completed / call.missed for automations and webhooks.
  4. Caller ID — before ringing the agent, the PBX may call GET /lookup?phone=… to show the client name on the phone or route the call to the client's owner (owner_extension).
  5. Click to call — set a Dial URL on the PBX side. When an agent clicks a phone number, the CRM sends
    {
        "action": "dial",
        "extension": "201",
        "number": "972521234567",
        "number_local": "0521234567",
        "client_id": 42,
        "user_email": "agent@company.co.il",
        "request_id": "uuid",
        "timestamp": 1700000000
    }
    signed like webhooks with the dial secret (X-RoboCRM-Signature). The PBX rings the extension and then dials the number. Without a Dial URL the CRM opens a tel: link (softphone / mobile).
# minimal Asterisk / FreePBX style hook (pseudo):
curl -X POST "https://crm.simulatefin.com/api/v1/calls" \
  -H "Authorization: Bearer rcrm_…" \
  -H "Content-Type: application/json" \
  -d '{"call_id":"${UNIQUEID}","status":"ringing","direction":"inbound","from":"${CALLERID(num)}","to":"${EXTEN}","extension":"${DIALEDPEERNUMBER}"}'

Errors

{"error": {"code": "validation_error", "message": "email is invalid"}, "field": "email"}
HTTPcode
400invalid_json
401unauthorized — missing, revoked or expired key
403insufficient_scope, forbidden
404not_found
422validation_error (with field)
429rate_limited