Getting started
What’s the base URL?
crosoftware.net-based URL, that’s stale: the API migrated to api.crosoftware.com.
What information do I need to provide to request credentials?
When you email crodev@rapidworks.com with the subject “CRO API Credentials Request”, including the following up front avoids a round trip:- Company / tenant name
- Primary use case (e.g. dispatch-side integration, reporting/BI, CRM sync)
- Landing/redirect URL: see Authentication Guide for what this is and why it’s required
- Requested role: see Roles for the available roles; tell us what your integration needs to do and we can advise if you’re unsure
- Any specific endpoints or data you already know you need
What redirect URL should I use?
Any URL you control, or the fixed OAuth redirect URL published by a third-party tool you’re routing data through (BI/reporting platforms and similar integrations typically have their own). We register it on our end when we provision your credentials. If you ever need to change it later, just reach out and we’ll update it.How do I find my tenant ID?
Once you can successfully authenticate, callGET /tenants: it returns the list of tenants your account has access to.
Is there a sandbox or test environment?
No, there is no separate sandbox. Integrations are built and tested directly against production data, so treat any write operations (creating jobs, customers, webhooks, etc.) with the same care you would in a live environment.How does pagination work, and can I control the page size?
List endpoints accept two query parameters:page_index (which page, default 1) and page_limit (results per page, default 100, maximum 1000). The response envelope’s current_page/current_limit fields mirror whatever you passed (or the defaults). Parameter names are consistent across list endpoints.
Are there any rate limits?
Please keep request rates reasonable for your use case. We may throttle or suspend clients engaging in excessive or abusive usage. If your integration has unusually high request volume, let us know so we can plan around it together.Authentication & authorization
Does the API support API keys instead of OAuth2?
No. The API uses OAuth2 exclusively (Authorization Code and Refresh Token grants); there is no API-key auth path. This is the most common blocker for integrations built through no-code tools (e.g. Zapier) that expect a simple API key. You’ll need an OAuth2-capable client or an intermediary that can complete the OAuth flow on your behalf.Why am I still getting a 403 even though I have the right role?
Some endpoints are gated by an OAuth scope in addition to the role check on your account. Having the right role (e.g. Admin or Dispatcher) is necessary but not always sufficient: your OAuth client also needs the specific scope for that resource. Scopes are bound to a token at issue time, not checked against some separate live permission set, so if a scope is added to your client after you’ve already obtained a token, you need to re-issue your token for the change to take effect. If you’re unsure whether a 403 is a role or scope issue, an OAuth introspection call (POST /oauth2/introspect with your token) will show you the scopes actually present on the token.
What’s the difference between first-party and third-party roles?
ThirdPartyDispatcher and ThirdPartyDriver are a parallel hierarchy to the first-party roles (Admin, Driver, Dispatcher, CrmUser), not a nested extension of them. Holding Admin does not grant access to endpoints restricted to a ThirdParty* role. There is no ThirdParty*-specific OAuth scope: third-party endpoints use the same api scope as everything else; access is controlled entirely by which role your account holds.
How do I get a ThirdParty* role issued?
ThirdPartyDispatcher accounts are provisioned by registering via POST /haulers. This is self-service and requires only captcha and email verification, no prior relationship needed. An existing customer can also send you a direct invite link from their own dispatch tool, which pre-fills the same registration flow. ThirdPartyDriver currently has no self-service path; contact us if your integration needs one.
Note: webhook access (/hooks) is restricted to first-party Admin/Dispatcher roles regardless of scope, so ThirdParty* accounts cannot register webhooks.
Webhooks
Can CRO register a webhook on our behalf?
No, webhook registration is self-service. You authenticate via OAuth and make thePOST /hooks call yourself; there’s no path for us to provision a hook directly on a client’s behalf, since the registration call itself requires the OAuth credentials you’d need anyway.
Does the “Job” event fire when a job is completed?
No. TheJob event fires on dispatch (when a job is assigned to a driver/route), not on completion. There is currently no webhook event for job completion. If you need to react to completed jobs, poll the jobs endpoint and filter for recently completed jobs (GET /locations/{location_id}/jobs).
What events are available?
| Event | Fires on | Payload |
|---|---|---|
Ping | Test delivery | {hook_id} |
Customer | Create | {customer_id} |
Customer | Update | {customer_id, modified_fields[]} |
CustomerLocation | Add / Delete | {customer_id, location_id} |
CustomerLocation | Update | {customer_id, location_id, modified_fields[]} |
Job | Dispatch | {job_id, location_id} |
Truck event type can be requested when creating a hook, but no publisher is wired up for it yet, so it will never actually deliver: treat it as not available. There are no billing events (invoices, payments). The Job payload carries only IDs. If you need customer name/email/phone, make a follow-up call to the jobs or customers endpoint using the job_id.
How do I verify a webhook’s signature?
Every delivery includes a headerX-Cro-Signature: sha256=<hex>: an HMAC-SHA256 of the raw request body, keyed with base64decode(secret), where secret is the value returned to you at hook-creation time.
The delivery envelope is:
What’s the retry/timeout behavior?
Failed deliveries are not currently retried. If your endpoint is unreachable or returns an error, that delivery attempt is not redelivered. The per-attempt timeout is 15 seconds. Since there’s no automatic retry, build your own reconciliation for events you can’t afford to miss (e.g. periodically polling the relevant resource as a backstop).How do I delete a webhook?
DELETE /hooks/{hook_id} deactivates the hook (a soft delete). It immediately stops receiving deliveries, but the hook record and its secret are retained rather than purged.
What are the constraints on a callback URL?
Maximum 255 characters, no IP allowlisting is enforced on our side. Use anhttp:// or https:// URL: deliveries are sent as a standard HTTP POST, so other URI schemes won’t receive anything even though the field doesn’t strictly reject them. HTTPS is strongly preferred.