RISE CRM REST API examples in cURL, PHP, Python and JavaScript, plus an importable Postman collection and a complete endpoint catalogue. This repository is the practical companion to REST API for RISE CRM (v2.1.0) by Themesic Interactive - the plugin that turns RISE CRM into an HTTP/JSON API you can call from anything: AI agents, n8n, Zapier, Make, spreadsheets, mobile apps or your own backend.
Every example here uses a real endpoint of the plugin, the real authtoken header, and no invented
fields: every field in every payload was checked against the controller that receives it, and where
a resource silently ignores a field the snippet says so instead of sending it. Copy, paste, run.
- Get the plugin: https://themesic.com/product/rest-api-plugin-for-rise-crm-connect-rise-with-third-party-applications/
- All Themesic products: https://themesic.com/products/
- Live spec on your own install:
GET /api/openapi.json- and a browsable UI atGET /api/swagger
Does RISE CRM have a REST API?
Yes. The REST API for RISE CRM
plugin adds a full RESTful HTTP/JSON API to RISE CRM: 35 CRM resources - 34 of them with list,
get, search, create, update and delete, plus activity-logs as a read-only audit trail - together
with 27 platform endpoints for MCP, webhooks, batch, automation, reporting, custom fields and
machine-readable specs, a set of small utility lookups, and the ten groups added in v2.1.0 (line
items, knowledge base, checklists, files, estimate and proposal comments, team, project members,
roles and templates). That is 54 endpoint groups and 322 documented endpoints in total.
How do I authenticate with the RISE CRM API?
Send your token in the authtoken HTTP header on every request. There is no query-parameter
fallback. See docs/authentication.md.
What is the base URL of the RISE CRM API?
https://yoursite.com/index.php/api - for example https://yoursite.com/index.php/api/clients. If
your install serves RISE without index.php in the URL, use https://yoursite.com/api.
Can I connect RISE CRM to ChatGPT, Claude or Cursor?
Yes. The plugin ships an MCP server at POST /api/mcp speaking JSON-RPC 2.0 (protocol version
2024-11-05) with 223 generated tools over the same 35 resources. It is opt-in: switch it on from
the API settings screen. See MCP server.
Can I connect RISE CRM to n8n, Zapier or Make?
Yes, two ways: polling triggers under /api/automation/*, or push-style webhooks with
HMAC-SHA256 signatures under /api/webhooks.
How do I create an invoice through the RISE CRM API?
POST /api/invoices with invoice_client_id, invoice_bill_date and invoice_due_date. See
snippets/curl/invoices.sh.
Is there a Postman collection for RISE CRM?
Yes - postman/rise-rest-api.postman_collection.json
in this repo, and your own install can generate one at GET /api/postman.json.
Can I run many API calls in one request?
Yes. POST /api/batch runs up to 50 operations in a single HTTP call, using the same tool names as
the MCP server.
| Folder | What is inside |
|---|---|
postman/ |
Importable Postman collection and environment with {{base_url}} and {{authtoken}} |
snippets/curl/ |
Copy-paste curl commands |
snippets/php/ |
PHP examples (built-in cURL extension, no dependencies) |
snippets/python/ |
Python examples (requests) |
snippets/javascript/ |
Node / browser examples (fetch) |
docs/ |
Authentication: creating and using a token |
Each language folder carries the same twelve examples: clients, leads, invoices, projects,
tasks, tickets, webhooks, mcp, batch, automation, custom_fields and list_features.
- In RISE CRM, activate the REST API plugin and open the API settings screen.
- Create an API user and copy its
authtoken. - Call the API:
curl -sS -H "authtoken: YOUR_API_TOKEN" \
"https://yoursite.com/index.php/api/clients"That returns your clients as JSON. Swap clients for any resource in the
catalogue below.
Create something:
curl -sS -X POST "https://yoursite.com/index.php/api/leads" \
-H "authtoken: YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"company_name":"Northwind Trading","owner_id":1,"lead_status_id":1,"lead_source_id":1}'- Postman > Import >
postman/rise-rest-api.postman_collection.json. - Import
postman/rise-rest-api.postman_environment.json. - Set
base_urltohttps://yoursite.com/index.php/apiandauthtokento your token. - Pick a request and hit Send. Authentication is applied collection-wide, so nothing else to wire up.
| Item | Value |
|---|---|
| Header | authtoken: YOUR_API_TOKEN |
| Base URL | https://yoursite.com/index.php/api |
Base URL without index.php routing |
https://yoursite.com/api |
| Query-parameter auth | Not supported - the header is the only accepted method |
Request bodies on POST |
application/json, multipart/form-data or application/x-www-form-urlencoded |
Request bodies on PUT / PATCH |
application/json or application/x-www-form-urlencoded. multipart/form-data is not supported on PUT or PATCH - PHP has no built-in parser for it on those verbs, so the body arrives empty |
| Update verbs | Every writable resource accepts both PUT and PATCH, and they behave identically |
curl -sS -H "authtoken: YOUR_API_TOKEN" "https://yoursite.com/index.php/api/clients"Tokens are issued per API user from the RISE API settings screen and are JWT values that expire,
so an integration should be able to pick up a refreshed token. Full details, including what each
failure message means, are in docs/authentication.md.
34 of the 35 CRM resources follow the same six routes under the base path
https://yoursite.com/index.php/api. activity-logs is the one exception: it is an audit trail
and exposes the three read routes only.
GET /api/:resource list
GET /api/:resource/:id get one
GET /api/:resource/search/:keyword search
POST /api/:resource create
PUT /api/:resource/:id update (PATCH works too)
DELETE /api/:resource/:id deleteTwo things to know before you build against list and search:
searchis capped at 10 rows. Every resource's search query ends withLIMIT 0, 10. There is no page, offset or total-count parameter on it. Search is the "turn a name into an id" helper; use the list endpoint or the batch tools to walk a whole collection.listreturns the whole collection and has nolimit,offsetorsortparameter. 20 of the 35 resources do read a small set of named exact-match query filters -GET /api/tasks?project_id=1,GET /api/invoice-payments?invoice_id=1,GET /api/users?user_type=staffand so on. The full per-resource list is insnippets/curl/list_features.sh, which also lists the columns each search endpoint really matches.
| # | Resource | Base path | Operations |
|---|---|---|---|
| 1 | Leads | /api/leads |
list, get, search, create, update, delete |
| 2 | Clients | /api/clients |
list, get, search, create, update, delete |
| 3 | Contacts | /api/contacts |
list, get, search, create, update, delete |
| 4 | Users | /api/users |
list, get, search, create, update, delete |
| 5 | Projects | /api/projects |
list, get, search, create, update, delete |
| 6 | Tasks | /api/tasks |
list, get, search, create, update, delete |
| 7 | Milestones | /api/milestones |
list, get, search, create, update, delete |
| 8 | Project comments | /api/project-comments |
list, get, search, create, update, delete |
| 9 | Tickets | /api/tickets |
list, get, search, create, update, delete |
| 10 | Ticket comments | /api/ticket-comments |
list, get, search, create, update, delete |
| 11 | Invoices | /api/invoices |
list, get, search, create, update, delete |
| 12 | Invoice payments | /api/invoice-payments |
list, get, search, create, update, delete |
| 13 | Estimates | /api/estimates |
list, get, search, create, update, delete |
| 14 | Estimate requests | /api/estimate-requests |
list, get, search, create, update, delete |
| 15 | Proposals | /api/proposals |
list, get, search, create, update, delete |
| 16 | Orders | /api/orders |
list, get, search, create, update, delete |
| 17 | Contracts | /api/contracts |
list, get, search, create, update, delete |
| 18 | Subscriptions | /api/subscriptions |
list, get, search, create, update, delete |
| 19 | Expenses | /api/expenses |
list, get, search, create, update, delete |
| 20 | Expense categories | /api/expense-categories |
list, get, search, create, update, delete |
| 21 | Items | /api/items |
list, get, search, create, update, delete |
| 22 | Item categories | /api/item-categories |
list, get, search, create, update, delete |
| 23 | Taxes | /api/taxes |
list, get, search, create, update, delete |
| 24 | Timesheets | /api/timesheets |
list, get, search, create, update, delete |
| 25 | Messages | /api/messages |
list, get, search, create, update, delete |
| 26 | Notes | /api/notes |
list, get, search, create, update, delete |
| 27 | Events | /api/events |
list, get, search, create, update, delete |
| 28 | Announcements | /api/announcements |
list, get, search, create, update, delete |
| 29 | Leave applications | /api/leave-applications |
list, get, search, create, update, delete |
| 30 | Todos | /api/todos |
list, get, search, create, update, delete |
| 31 | Attendance | /api/attendance |
list, get, search, create, update, delete |
| 32 | Payment methods | /api/payment-methods |
list, get, search, create, update, delete |
| 33 | Labels | /api/labels |
list, get, search, create, update, delete |
| 34 | Notifications | /api/notifications |
list, get, search, create, update, delete |
| 35 | Activity logs | /api/activity-logs |
list, get, search (read-only audit trail) |
That is the complete list: 35 CRM resources, 34 with full CRUD plus search and activity-logs with
the three read routes. Routes.php backs this up with 35 DELETE routes - one per writable CRM
resource, plus one for webhooks.
Small read-only helper endpoints that resolve the ids the create and update calls need. They are not CRM resources and have no CRUD of their own:
| Endpoint | Returns |
|---|---|
GET /api/client_groups |
Client groups |
GET /api/project_labels |
Project labels |
GET /api/invoice_labels |
Invoice labels |
GET /api/ticket_labels |
Ticket labels |
GET /api/invoice_tax |
Invoice taxes |
GET /api/contact_by_clientid/:id |
Contacts of one client |
GET /api/ticket_type |
Ticket types |
GET /api/staff_owner |
Staff members who can own a record |
GET /api/project_members |
Project members |
| Group | Endpoints |
|---|---|
| MCP (2) | POST /api/mcp, GET /api/mcp/info |
| Webhooks (8) | GET /api/webhooks, GET /api/webhooks/events, GET /api/webhooks/:id, GET /api/webhooks/:id/logs, POST /api/webhooks, POST /api/webhooks/:id/toggle, PUT /api/webhooks/:id, DELETE /api/webhooks/:id |
| Reporting (5) | GET /api/reports, /api/reports/summary, /api/reports/invoices, /api/reports/projects, /api/reports/timesheets |
| Custom fields (4) | GET /api/custom-fields, GET /api/custom-fields/:id, GET /api/custom-fields/values/:relatedTo/:id, PUT /api/custom-fields/values/:relatedTo/:id |
| Automation (3) | GET /api/automation/resources, /api/automation/sample/:resource, /api/automation/poll/:resource |
| Tooling (3) | GET /api/postman.json, GET /api/swagger, GET /api/playground |
| OpenAPI (1) | GET /api/openapi.json |
| Batch (1) | POST /api/batch |
Ten more endpoint groups sit alongside the 35 CRM resources above - 54 endpoint groups and 322
documented endpoints in total. They use the same authtoken header and base URL.
| Group | Endpoints |
|---|---|
| Line items (on sales documents) | GET / POST / PUT / PATCH /api/:parent/:id/items and DELETE /api/:parent/:id/items/:itemId, where :parent is invoices, estimates, proposals, orders, contracts or subscriptions |
| Knowledge base | Articles: GET / POST /api/knowledge-base/articles, GET / PUT / DELETE /api/knowledge-base/articles/:id, GET /api/knowledge-base/articles/search/:keyword, and GET / POST /api/knowledge-base/articles/:id/helpful. Categories: the same CRUD + search under /api/knowledge-base/categories |
| Checklists | items, templates and groups, each with full CRUD + search under /api/checklists/{items,templates,groups}; plus GET /api/checklists/groups/:id/templates, POST /api/checklists/items/apply-group and GET /api/checklists/tasks/:task_id/summary |
| Files | GET / POST /api/files, GET / DELETE /api/files/:source/:id, GET /api/files/:source/:id/download, GET /api/files/search/:keyword, GET /api/files/folders, GET /api/files/categories |
| Estimate comments | /api/estimate-comments - full CRUD + search |
| Proposal comments | /api/proposal-comments - full CRUD + search |
| Team | /api/teams - full CRUD + search, plus GET /api/teams/:id/members |
| Project members | /api/project-members - full CRUD + search |
| Roles (read-only) | GET /api/roles, GET /api/roles/:id, GET /api/roles/search/:keyword |
| Templates (read-only) | GET /api/templates (types), GET /api/templates/:type, GET /api/templates/:type/:id, GET /api/templates/:type/search/:keyword |
The machine-readable openapi/openapi.json and the Postman collection cover
every group, including these.
POST /api/mcp is a Model Context Protocol server over Streamable HTTP, speaking JSON-RPC 2.0
with protocol version 2024-11-05. A full-access token sees 223 tools: 207 across the 35
resources (clients_list, clients_get, clients_search, invoices_create, tasks_update,
tickets_delete - activity_logs contributes read tools only), 14 lookup catalogues such as
list_task_statuses and list_lead_sources, and the 2 utilities get_current_datetime and
get_server_info. So Claude Desktop, ChatGPT, Cursor and the n8n AI Agent can read and write your
CRM.
<resource>_search is a different implementation from the REST /search/:keyword route: it takes a
limit (default 25, maximum 100) instead of the REST route's fixed cap of 10, and it matches only
the resource's own table, so its column list differs from the REST one. Both are documented per
resource in snippets/curl/list_features.sh.
The MCP server is opt-in and disabled by default. Turn it on from the API settings screen
(setting api_mcp_enabled); while it is off, the endpoint answers 403 with a JSON-RPC error.
Supported methods: initialize, ping, tools/list (a single page, no cursor pagination) and
tools/call. A payload made only of notifications/* messages returns 202 with an empty body.
# 1. Handshake
curl -sS -X POST "https://yoursite.com/index.php/api/mcp" \
-H "authtoken: YOUR_API_TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'
# 2. Discover every tool
curl -sS -X POST "https://yoursite.com/index.php/api/mcp" \
-H "authtoken: YOUR_API_TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
# 3. Call one
curl -sS -X POST "https://yoursite.com/index.php/api/mcp" \
-H "authtoken: YOUR_API_TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"clients_search","arguments":{"q":"Acme","limit":5}}}'A tool failure comes back as a normal result with isError: true rather than a protocol error, so
the agent can read the message and react. Full examples:
snippets/curl/mcp.sh ·
snippets/php/mcp.php ·
snippets/python/mcp.py ·
snippets/javascript/mcp.js
Webhooks are managed over REST and delivered asynchronously.
GET /api/webhooks/eventsis the authoritative event catalogue, derived from the resources that really exist on your install. Every writable resource contributes<resource>.created,<resource>.updatedand<resource>.deleted.- Three subscription shapes: the exact name (
invoices.created), a resource wildcard (invoices.*) and the global wildcard (*). - Every delivery carries
X-Rise-Signature: t=<unix>,v1=<hex hmac_sha256 of "{t}.{raw body}">, keyed with the signing secret. Verify it by recomputing the HMAC over the exact bytes you received, comparing in constant time, and rejecting atoutside your tolerance window. - The signing secret is returned once, when the webhook is created or when you rotate it with
rotate_secret. No endpoint can show it again. - Target URLs are SSRF-checked before saving and again before every delivery: loopback, link-local, cloud metadata (169.254.0.0/16), RFC1918 and their IPv6 equivalents are refused, redirects are never followed and TLS verification is always enforced.
- Delivery is queued and drained by the RISE cron job, with exponential backoff on failure. If deliveries queue but nothing arrives, check that the RISE cron job is running.
GET /api/webhooks/:id/logsshows every attempt: HTTP status, attempt number, response snippet, error reason, duration and timestamp. The most recent 500 attempts per webhook are kept.
curl -sS -X POST "https://yoursite.com/index.php/api/webhooks" \
-H "authtoken: YOUR_API_TOKEN" -H "Content-Type: application/json" \
-d '{"name":"Ops sync","url":"https://hooks.example.com/rise","events":["invoices.created","tickets.*"],"timeout":10,"retry_count":1}'Signature verification in PHP, Python and JavaScript is in the webhooks snippet of each language
folder.
POST /api/batch runs up to 50 operations in the order you send them, using the same tool names
as the MCP server. Batch does not require the MCP server and ignores api_mcp_enabled; it has its
own switch, api_batch_enabled, which is on unless it is explicitly turned off.
curl -sS -X POST "https://yoursite.com/index.php/api/batch" \
-H "authtoken: YOUR_API_TOKEN" -H "Content-Type: application/json" \
-d '{
"stop_on_error": false,
"operations": [
{"tool":"clients_create","arguments":{"data":{"company_name":"Acme Corporation","city":"San Francisco"}}},
{"tool":"invoices_get","arguments":{"id":5}},
{"tool":"taxes_list","arguments":{"limit":2}}
]
}'The response is always 200 even when an operation fails: read the per-item ok flag and the
completed / failed / skipped counters. Send stop_on_error: true to abort at the first
failure. Batch operations are not transactional - earlier successful writes are not rolled back.
Three read-only endpoints implement the polling-trigger contract every no-code platform expects:
| Endpoint | Purpose |
|---|---|
GET /api/automation/resources |
Which resources can be polled, their cursor columns, and whether they detect updates |
GET /api/automation/sample/:resource |
One representative record, for field mapping in the connector UI |
GET /api/automation/poll/:resource |
The records created or changed since a cursor, newest first |
# First run: no cursor, returns the last 24 hours
curl -sS -H "authtoken: YOUR_API_TOKEN" \
"https://yoursite.com/index.php/api/automation/poll/tickets?limit=50"
# Later runs: send back meta.next_since from the previous response
curl -sS -H "authtoken: YOUR_API_TOKEN" \
"https://yoursite.com/index.php/api/automation/poll/tickets?since=2026-07-21T09:45:12%2B00:00&limit=50"De-duplicate on id: the cursor is inclusive and some RISE columns store a date without a time, so
a record can legitimately arrive twice.
/api/automation/poll is not paginated. There is no offset parameter: the endpoint always
answers with the newest limit rows on or after the cursor, starting at offset 0. Asking again for
the same cursor with the same limit therefore returns exactly the same rows, for ever - so never
write while (has_more) { poll(same cursor) }. meta.has_more means only "the page came back full,
so older matches may have been cut off". The two honest reactions are to raise limit (100 is the
ceiling) or to poll more often so one window fits in one page. The automation snippet in each
language folder shows the bounded version.
meta.detects_updates tells you honestly whether the resource can fire on updates - RISE has no
generic "modified" timestamp, so only tables with a column that moves when a record is touched (a
ticket's last activity, a task's status change, an invoice's cancellation) do.
Read-only aggregates for dashboards and BI tools:
curl -sS -H "authtoken: YOUR_API_TOKEN" "https://yoursite.com/index.php/api/reports"
curl -sS -H "authtoken: YOUR_API_TOKEN" "https://yoursite.com/index.php/api/reports/summary"
curl -sS -H "authtoken: YOUR_API_TOKEN" "https://yoursite.com/index.php/api/reports/invoices?from=2026-01-01&to=2026-03-31"
curl -sS -H "authtoken: YOUR_API_TOKEN" "https://yoursite.com/index.php/api/reports/timesheets?group_by=project&limit=50"Custom fields expose their definitions and the per-record values:
curl -sS -H "authtoken: YOUR_API_TOKEN" "https://yoursite.com/index.php/api/custom-fields?related_to=clients"
curl -sS -H "authtoken: YOUR_API_TOKEN" "https://yoursite.com/index.php/api/custom-fields/values/clients/12"See snippets/curl/custom_fields.sh for writing values back.
| Endpoint | What you get |
|---|---|
GET /api/openapi.json |
OpenAPI 3.0 document for the whole API |
GET /api/postman.json |
Postman v2.1 collection generated from your own install |
GET /api/swagger |
Swagger UI, ready to browse |
GET /api/playground |
Interactive API playground |
Point any OpenAPI code generator at /api/openapi.json to produce a typed client in your language.
- AI assistants (MCP) - let Claude Desktop, ChatGPT or Cursor query and update RISE through
/api/mcp. - n8n, Zapier, Make - polling triggers via
/api/automation/*, or push via webhooks. - Slack, Discord and internal services - HMAC-signed webhook deliveries on invoice, ticket, lead and project events.
- Dashboards and BI -
/api/reports/*returns pre-aggregated figures so you do not query the CRM database directly. - Data migration and bulk edits -
POST /api/batchmoves 50 operations per request. - Custom portals and mobile apps - build on top of clients, projects, tasks, tickets and invoices.
{"status":false,"message":"Token is not defined."}
The authtoken header was missing. It is a header, not a query parameter, and the name is
authtoken (case-insensitive), not Authorization.
{"status":false,"message":"Token Time Expire."}
The token has passed its expiry window. Generate a fresh token from the API settings screen.
{"status":false,"code":404,"message":"Route not found"}
The path does not exist. Check the spelling and the hyphens: platform and multi-word resources use
hyphens (invoice-payments, custom-fields, leave-applications), never underscores. Note that the
MCP and batch tool names use underscores (invoice_payments_list) while the URLs use hyphens.
Everything 404s, including /api/clients
Your install probably needs index.php in the URL. Use
https://yoursite.com/index.php/api/clients.
404 Not Found with {"status":false,"messages":{"error":"No data were found"}}
The request was authenticated and routed fine, the resource simply has no matching rows. An empty
list is reported as a 404 by the CRUD endpoints, so treat it as "empty", not as "broken".
MCP returns 403 with -32000 MCP server is disabled.
The MCP server is opt-in. Enable it from the API settings screen (api_mcp_enabled).
Batch returns 403
api_batch_enabled has been set to 0. It defaults to on.
Webhook created, but nothing is delivered
Deliveries are queued and sent by the RISE cron job. Confirm the cron job runs, then read
GET /api/webhooks/:id/logs - blocked deliveries are logged there with the reason.
Webhook rejected with "the host resolves to ..." The SSRF policy refuses private, loopback, link-local and cloud-metadata addresses. A webhook target must be a publicly resolvable http or https URL.
A POST is rejected with a validation error listing a field I did send
Check the field name against the snippet. Some create payloads use prefixed names, for example an
invoice takes invoice_client_id, invoice_bill_date and invoice_due_date.
Lead creation fails on company_name
Lead names are validated as letters and spaces only. Use a plain name without punctuation.
A field I sent on POST came back empty
Create and update do not always read the same fields. The ones that catch people out: an invoice's
invoice_note and labels are read by update only, a client's group_ids is validated on create
but stored by update only, and a ticket has no description on create at all - the body of a ticket
is a separate POST /api/ticket-comments. created_by is honoured by announcements, contacts,
estimate-requests, estimates, events, expenses, invoices, invoice-payments, items,
leave-applications, notes, orders, project-comments, projects, proposals and todos; elsewhere it is
ignored. The snippets in this repo call each of these out where they occur.
A PUT or PATCH arrived with an empty body
You almost certainly sent it as multipart/form-data. That works on POST but not on PUT or
PATCH - PHP has no built-in parser for multipart on those verbs. Send JSON or
application/x-www-form-urlencoded instead.
Signature verification always fails
Recompute the HMAC over the raw request body exactly as received, before any JSON parsing or
re-serialisation, and sign the string "{t}.{raw body}" where t is the timestamp from the header.
This repository is an examples companion to the commercial plugin:
REST API for RISE CRM - connect RISE with third-party applications by Themesic Interactive, version 2.1.0.
- Buy or learn more: https://themesic.com/product/rest-api-plugin-for-rise-crm-connect-rise-with-third-party-applications/
- All products: https://themesic.com/products/
Contributions of extra examples are welcome - see CONTRIBUTING.md.
Example code in this repository is released under the MIT License. "RISE CRM" is a trademark of its respective owner; the REST API plugin is a commercial product by Themesic Interactive.