Home/Developers
Partner API — connect guide
Hand this page (or the prompt below) to another project’s AI or engineer. Everything needed to authenticate and call the Partner REST API is here — no tenant-specific secrets.
For another project’s AI
Copy the prompt, replace nothing except the host if needed, and paste it into the other chat. It points at OpenAPI, auth, scopes, and the charges endpoints.
Ready-to-paste prompt
You are integrating with the Carmasy Partner REST API.
Base URL: https://www.carmasy.com/api/v1/api/v1
Auth: every request needs header X-API-Key: apk_… (issued in the organization admin: Settings → API). Never put the key in client-side JS or git.
Machine-readable OpenAPI: https://www.carmasy.com/api/openapi (v1.10.0+)
Interactive Swagger: https://www.carmasy.com/api-docs
Human connect guide: https://www.carmasy.com/developers
Human inventory guide: docs/api-vehicles-guide.md (in the platform repo)
Important scopes:
- catalog:write → PUT /vehicles (full fleet snapshot into main fleet; required first), PATCH /vehicles/:id
- catalog:read → GET /vehicles, GET /vehicles/:id, GET /vehicles/versions
- webhooks:manage → POST/GET /partner-webhooks (complete compliance snapshots only)
- charges:fines:search → POST /charges/fines/search, GET /charges/fines/sync-status
- charges:roads:search → POST /charges/roads-parking/search, GET /charges/roads-parking/statement, GET /charges/roads-parking/sync-status
Register the fleet once (PUT /vehicles with make/model/plate/externalId), configure a signed webhook, then receive compliance.*.snapshot.completed pushes. Manual fines/roads search is for diagnostics.
See docs/api-partner-vehicle-registry.md.
Fines search body example:
{"vehicleIds":["11111111-1111-4111-8111-111111111111"]}
or legacy plates:
{"plates":[{"number":"12345","code":"B","emirate":"DUBAI"}],"dateFrom":"2026-06-01T10:00:00+04:00","dateTo":"2026-06-20T18:00:00+04:00"}
(dateFrom/dateTo optional; if both set → ISO-8601 with offset, max 90 days.)
Partial provider failures return 200 with metadata.complete=false, metadata.plates[].status, and warnings like plate_unresolved:B:12345.
Salik roads/parking — TWO methods (same scope charges:roads:search). Never send Salik username/password in the body (office vault only).
1) Line items for a rental/time window:
POST /charges/roads-parking/search
{"plates":[{"number":"89560"},{"salikTag":"13528686"}],"kinds":["roads","parking"],"dateFrom":"2026-07-01T00:00:00+04:00","dateTo":"2026-07-31T23:59:59+04:00"}
dateFrom/dateTo REQUIRED (ISO-8601 with offset, max 90 days). If both tag and number are sent, salikTag wins.
Platform pulls Salik Excel for the calendar days, then filters to requested plates/tags.
2) Monthly statement (сверка) for one calendar month — account totals + per-vehicle aggregates:
GET /charges/roads-parking/statement?period=202607
Optional filter: plates=[{"number":"87680","code":"FF"},{"salikTag":"13755923"}] (URL-encoded JSON).
Returns account payments/tolls/parking/balances plus data.vehicles[] (toll/parking counts & amounts per plate/tag).
Use this for month-end per-vehicle reconciliation — do not replace it with summing search rows.
3) Health: GET /charges/roads-parking/sync-status → salik.hasCredentials + lastCheck.
Client timeout ≥ 120s recommended for search and statement.
Errors: 401 bad/missing key, 403 missing scope, 400 bad body/query, 409 salik_not_connected, 422 salik_auth_failed (do not retry — update office vault), 429 rate limit (20 searches / 15 min per kind per org), 503 salik_unavailable (retryable — respect Retry-After).
Start by GET https://www.carmasy.com/api/v1/charges/fines/sync-status and GET https://www.carmasy.com/api/v1/charges/roads-parking/sync-status, then one short fines search, one roads search, and one statement?period=YYYYMM.Canonical links
- OpenAPI JSON
Machine-readable spec (v1.13.0) — best for codegen and AI tools
- Swagger UI
Interactive catalog — Try it with your API key
- This connect guide
Human + AI onboarding entry point
Connect in four steps
01
Get an organization API key
In the office: Settings → API. Create a key and select only the scopes you need. The secret is shown once — store it server-side.
02
Enable product modules
Charges scopes require the matching modules (fines, roads/parking). Without the module, key creation and calls return entitlement errors.
03
Call with X-API-Key
Every Partner request under /api/v1/* needs header X-API-Key: apk_…. The key is bound to one organization — you only see that org’s data.
04
Use OpenAPI as source of truth
Fetch /api/openapi for schemas and paths. Use this page for the connect story; use Swagger for exploration.
Authentication
Header only — no OAuth for Partner v1. Missing/invalid key → 401. Valid key without the required scope → 403.
X-API-Key: apk_<secret>Base URL
Use the organization host + /api/v1. Example shape: https://{org-host}/api/v1. OpenAPI and Swagger live on the same origin.
https://www.carmasy.com/api/v1Scopes
Pick scopes when creating the key. Charges search is separate from read catalog scopes.
| Scope | Endpoints |
|---|---|
| catalog:read | GET /vehicles, GET /vehicles/:id, GET /vehicles/versions |
| catalog:write | PUT /vehicles, PATCH /vehicles/:id |
| tariffs:read | GET /tariffs/rental, GET /tariffs/lease |
| customers:read | GET /customers, GET /customers/:id, nested summaries |
| maintenance:read | GET /maintenance, GET /maintenance/:id |
| reservations:read | GET /reservations, GET /reservations/:id |
| agreements:read | lease / purchase / rental agreements |
| invoices:read | GET /invoices, GET /invoices/:id |
| availability:read | GET /fleet/availability, …/free |
| webhooks:manage | POST/GET /partner-webhooks |
| charges:fines:search | POST /charges/fines/search, GET …/sync-status |
| charges:roads:search | POST /charges/roads-parking/search, GET /charges/roads-parking/statement, GET …/sync-status |
Fines, roads & parking
External systems send plates (and optional rental window) or request a monthly Salik statement. Office UI does not replace these Partner endpoints. Salik passwords never go in the request body.
Fines — POST /charges/fines/search
- Scope: charges:fines:search
- Body: plates[{ number, code? }] required; dateFrom/dateTo optional (ISO-8601 with offset; max 90 days when both set)
- Optional customerId must belong to the same organization
- Impound buyout is a separate row: ticketNo `{policeTicket}-IMPOUND`, impound=true — sum fine + buyout for total owed
curl -sS -X POST "https://www.carmasy.com/api/v1/charges/fines/search" \
-H "X-API-Key: apk_…" \
-H "Content-Type: application/json" \
-d '{"plates":[{"number":"12345","code":"B"}]}'Roads & parking — two methods
- Scope: charges:roads:search for search, statement, and sync-status
- POST /charges/roads-parking/search — trip/parking line items for a time window (dateFrom + dateTo required, ISO with offset, max 90 days)
- Body plates: salikTag and/or number; if both set, salikTag wins
- GET /charges/roads-parking/statement?period=YYYYMM — monthly statement: account totals + vehicles[] per plate/tag (optional plates JSON filter)
- Use search for rental-window line items; use statement for month-end per-vehicle reconciliation — do not substitute one for the other
- GET /charges/roads-parking/sync-status — last check + salik.hasCredentials (no secrets)
- 409 salik_not_connected when Salik is not saved in office Integrations
- 422 salik_auth_failed when saved Salik username/password are rejected — update vault, do not retry
- 503 salik_unavailable is retryable (Retry-After); client timeout ≥ 120s recommended
curl -sS -X POST "https://www.carmasy.com/api/v1/charges/roads-parking/search" \
-H "X-API-Key: apk_…" \
-H "Content-Type: application/json" \
-d '{
"plates":[{"number":"89560"},{"salikTag":"13528686"}],
"kinds":["roads","parking"],
"dateFrom":"2026-06-01T00:00:00+04:00",
"dateTo":"2026-06-30T23:59:59+04:00"
}'Sync status
- GET /charges/fines/sync-status — last check + providerConfigured
- GET /charges/roads-parking/sync-status — last check + salik.hasCredentials (no secrets)
curl -sS "https://www.carmasy.com/api/v1/charges/fines/sync-status" -H "X-API-Key: apk_…"
curl -sS "https://www.carmasy.com/api/v1/charges/roads-parking/sync-status" -H "X-API-Key: apk_…"HTTP status codes
| Code | When |
|---|---|
| 200 | Success |
| 400 | Bad body/query (missing offset on dates, window > 90 days, invalid period, invalid_customer) |
| 401 | Missing or invalid API key |
| 403 | Key valid but scope or product module missing |
| 404 | Record not found or belongs to another organization |
| 409 | Salik not connected (roads-parking search or statement) |
| 422 | Salik credentials rejected (salik_auth_failed) or statement could not be parsed |
| 429 | Partner search rate limit exceeded |
| 503 | Salik portal temporarily unavailable (retryable) |
| 500 | Server error |
Limits
- Partner plate search: 20 requests / 15 minutes per organization, per kind (fines vs roads-parking)
- Date window for charges search: maximum 90 days
- Keys are server-to-server only — rotate or revoke from Settings if leaked
Security
- Never commit apk_ keys or put them in browser code
- Never send Salik username/password in Partner JSON
- Rotate creates a new key with the same scopes and revokes the old one
What else is on Partner API
Read-only operational data plus charges search and Salik statement. Full schemas: OpenAPI.
| Module | Scope | Endpoints |
|---|---|---|
| Fleet catalog | catalog:read | GET /vehicles |
| Tariffs | tariffs:read | GET /tariffs/* |
| Customers | customers:read | GET /customers |
| Reservations | reservations:read | GET /reservations |
| Agreements | agreements:read | GET /*-agreements |
| Invoices | invoices:read | GET /invoices |
| Availability | availability:read | GET /fleet/availability |
| Fines search | charges:fines:search | POST /charges/fines/search |
| Roads & parking | charges:roads:search | POST /charges/roads-parking/search, GET /charges/roads-parking/statement |