MCP server
If you work with an AI coding assistant, you can connect it to the Logistics API through our Model Context Protocol server. Your assistant can then search this documentation, read the API contract, and look up your own parcels, without you pasting anything into a chat window.
There is nothing to install. The server runs alongside the API and you authenticate with the same credentials you already use.
Connecting
Connecting takes four steps. The first three are the same ones you follow for
the rest of the API: if you already have a valid access token for the
environment you want, go straight to step 4.
1. Choose the environment
Decide first whether you connect to sandbox or to production: everything that
follows, from the credentials to the endpoint, belongs to one of them.
| Environment | MCP endpoint | Where you get the credentials |
|---|---|---|
| Sandbox | https://logistics.api.cabify-sandbox.com/mcp | Cabify Logistics Sandbox |
| Production | https://logistics.api.cabify.com/mcp | Cabify Logistics |
The server runs on the same host as the rest of the API and speaks MCP over
its Streamable HTTP transport, so it only answers POST.
We recommend starting with sandbox, where your assistant can only ever see
test data.
2. Get your API credentials
You need an API key for the environment you chose: a client id and a client
secret. If you do not have one yet, follow
Get an API key in that environment's portal. A key
created in one environment does not work in the other.
3. Get an access token
The server does not accept the client id and secret directly: exchange them
for an access token at the authorization endpoint, as for any other API call.
Sandbox:
curl -X POST -d "grant_type=client_credentials&client_id=<your-client-id>&client_secret=<your-client-secret>" \
--url https://logistics.api.cabify-sandbox.com/auth/api/authorizationProduction:
curl -X POST -d "grant_type=client_credentials&client_id=<your-client-id>&client_secret=<your-client-secret>" \
--url https://logistics.api.cabify.com/auth/api/authorizationThe response carries the token in access_token:
{
"access_token": "alfanumeric_example_access_token",
"refresh_token": "alfanumeric_example_refresh_token",
"expires_in": 2591999,
"token_type": "Bearer"
}Keep it in an environment variable named after its environment, which is what
the commands in step 4 read:
export DELIVERY_API_SANDBOX_TOKEN="alfanumeric_example_access_token"
# or, for production:
export DELIVERY_API_PRODUCTION_TOKEN="alfanumeric_example_access_token"The token expires after expires_in seconds (about 30 days). When it does,
the server answers 401 with "error": "invalid_token", and your assistant
usually shows the server as failed or disconnected: get a new token, as explained in
Getting started, and update it in your
assistant's configuration.
4. Add the server to your assistant
Add one server per environment, named after it. If you only use one
environment, add only that one; if you use both, for instance building against
sandbox and then checking real shipments in production, add both rather than
switching a single one back and forth.
With Claude Code:
claude mcp add --transport http delivery-api-sandbox https://logistics.api.cabify-sandbox.com/mcp \
--header "Authorization: Bearer ${DELIVERY_API_SANDBOX_TOKEN}"
claude mcp add --transport http delivery-api-production https://logistics.api.cabify.com/mcp \
--header "Authorization: Bearer ${DELIVERY_API_PRODUCTION_TOKEN}"With any client that reads a JSON configuration, such as Cursor:
{
"mcpServers": {
"delivery-api-sandbox": {
"url": "https://logistics.api.cabify-sandbox.com/mcp",
"headers": {
"Authorization": "Bearer <your-sandbox-access-token>"
}
},
"delivery-api-production": {
"url": "https://logistics.api.cabify.com/mcp",
"headers": {
"Authorization": "Bearer <your-production-access-token>"
}
}
}
}Why one server per environment:
- You always know which environment answered. Assistants tell tools apart
by the server they belong to, so every call names its environment, and you
can ask for one explicitly: "check this parcel in sandbox". - Nothing gets out of step. The host and the token change together, so you
cannot end up with the production host and a sandbox token, or reading one
environment while believing it is the other. - Production stays a deliberate choice. The server is read-only, but in
production your assistant reads your real shipments. Keep the production
server disabled until you need it: most assistants let you switch a server
off without removing it. - Each token expires on its own, so renewing one never breaks the other.
To check that it works, ask your assistant something like "list the hubs in
my sandbox account". If it cannot reach the server, try the endpoint
directly: this request should answer with the server's name and protocol
version.
curl -X POST https://logistics.api.cabify-sandbox.com/mcp \
-H "Authorization: Bearer ${DELIVERY_API_SANDBOX_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}'A 401 means the token is wrong or expired (step 3), or that it belongs to
the other environment. A 403 means the Authorization: Bearer <token>
header did not reach the server.
What your assistant can do
Read the documentation
| Tool | What it does |
|---|---|
search_docs | Searches this documentation and the API contract. |
read_doc | Reads one page or one specification file. |
list_docs | Lists what is available. |
openapi_operations | Lists the API operations. |
openapi_operation | Shows the request and response of one operation. |
error_code | Explains an error the API returned, and what to do about it. |
Look up your account
| Tool | What it does |
|---|---|
get_parcel | Reads one of your parcels by id. |
get_parcel_by_external_id | Finds your parcels by the external_id you gave them — your own order number. |
track_parcel | The state timeline of one of your parcels. |
search_parcels | Lists your parcels in the states you ask for. At least one state is required. |
list_shipping_types | The shipping types available to you at a pickup location. |
estimate_delivery | Price and timings for a shipment, before you create it. |
check_availability | Whether anything can be shipped from a pickup point for your account, before you create anything. |
get_proof_config | Which proofs of delivery are active on one of your parcels. |
get_label | Whether a printable label is ready for a parcel, and where to download it. |
list_webhooks | The webhook subscriptions you have configured. |
list_hubs | The hubs registered under your account. |
get_hub | Reads one of your hubs by the external id you gave it. |
Work out why something fails
| Tool | What it does |
|---|---|
diagnose_shipment | Checks what your account can ship from a pickup location and, given a parcel id, what state that parcel is in, and reports the likely cause and the next step. |
This is the one to reach for when a request failed and the error alone does not
say why, because the cause is more often coverage or account configuration than
a malformed payload. It takes coordinates, not an address, and it does not
check whether a driver is free at this moment -- so a location it reports as
fine can still fail momentarily.
These are scoped to the credentials you connect with: your assistant can only ever see your own data.
What it cannot do
The server is read-only. Creating, shipping and cancelling parcels stay on the REST API, so an assistant can look up how an operation works and prepare the call, but it cannot ship anything on your behalf.
Good to know
- The server answers one JSON-RPC message per request, or a batch of at most 20. It never initiates messages and keeps no session, so
GETandDELETEon the endpoint are declined. - Calls count towards the same rate limits as the rest of the API.
- If a tool fails, the reason comes back in the tool result rather than as an HTTP error, which is how MCP reports it.
Updated 2 days ago
