Connect Cypress
Let an organization's admin connect your app to Cypress, get tokens that act for that admin or for the agents your app adds, and sign people in with their Cypress login.
TL;DRAI summary
This chapter explains how your app can securely connect to Cypress using OAuth 2.1, allowing fleet managers or dealers to approve your app and grant it access to their organization's data. You'll learn how to register your app, manage agents, and use tokens to interact with the Cypress API.
An API key belongs to one organization and acts as that organization. Connect Cypress is for an app that many organizations use: an organization's admin approves your app once, on a Cypress screen, and your app gets tokens for that organization. It is OAuth 2.1 with PKCE. Since 1.5.0.
Your app can also ask to add agents to the organization's team. An agent is
a member of the organization with its own name and the role AGENT. Every
action it takes is recorded under its own name, and it works under the same
rules as everyone else, plus a few of its own (see What agents may not do).
Register your app
Apps are registered by Cypress. Send us:
| Field | What it is |
|---|---|
| Name and description | What the admin reads on the consent screen |
| Redirect addresses | Where Cypress sends the admin back. https only (http only for localhost), matched exactly; an http address on the person's own computer matches on any port |
| Scopes | The most your app will ever ask for, from the key scopes |
| Agents | Optional. The agents your app may add, each with a key, a name and its scopes, for example sell, "Cedar Sell", offers:write |
You receive a client_id (cyp_app_…) and, for an app that runs on a server,
a client_secret (cyp_cs_…), shown once. An app without a secret (a browser
or mobile app) uses PKCE alone and cannot add agents.
The server's settings are published at /.well-known/oauth-authorization-server.
Without registering: a client ID metadata document
An MCP client, or any app that runs on a person's own computer, can connect
without registering. Its client_id is the https address of a JSON document
it publishes about itself, as the MCP authorization flow (2026-07-28) has it.
The server's settings say client_id_metadata_document_supported: true.
{
"client_id": "https://desk.example.com/oauth/client.json",
"client_name": "Fleet Desk",
"client_uri": "https://desk.example.com",
"redirect_uris": ["http://127.0.0.1/callback", "https://desk.example.com/oauth/callback"],
"token_endpoint_auth_method": "none",
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"]
}- The address is
https, names a document (a path, not a whole site), carries no user name, password or fragment, and is written in its plain form. The document'sclient_idis that address, exactly. - Cypress reads it when an authorization starts, again after five minutes at most. It must be served at the address itself (a redirect is not followed), from a public address, as JSON of at most 5 KB, within five seconds.
- It is a public app: no secret (
token_endpoint_auth_methodisnoneor left out), PKCE always, and no agents. Its token acts for the admin who approved it, with the scopes they approved. - Redirect addresses are
https, orhttpon the person's own computer (127.0.0.1,localhost,[::1]). An address on the computer matches on any port, so a desktop client listshttp://127.0.0.1/callbackand listens on the port it picks. - The admin sees the address. The consent screen shows it beside the name, marks the app Not registered with Cypress and says where the browser returns; Settings → Connected apps shows it too. Cypress can switch an app off, and it then stays off whatever its document says.
The flow below is the same: send client_id (the address) and no secret when
you exchange the code.
Registering itself: dynamic client registration
An app that registers itself (RFC 7591), as Microsoft Copilot Studio's
automatic OAuth does, posts its settings to POST /api/oauth/register, the
server settings' registration_endpoint, and gets its client_id at once:
{
"client_name": "Fleet Desk",
"redirect_uris": ["https://desk.example.com/oauth/callback"],
"token_endpoint_auth_method": "none",
"grant_types": ["authorization_code", "refresh_token"],
"scope": "offers:read crm:read"
}- It answers
201with theclient_id(cyp_dyn_…), and for an app that asks forclient_secret_basicorclient_secret_post(the default) aclient_secret(cyp_cs_…), shown once and never expiring. - What it may ask for: the
authorization_codegrant, withrefresh_token;response_typescode; any of the scopes (all of them whenscopeis left out), of which an admin approves a part. It never adds agents. - Redirect addresses follow the same rule as above:
https, orhttpon the person's own computer, without a fragment. - A refusal answers
400withinvalid_redirect_uriorinvalid_client_metadataand says why. Twenty registrations an hour per caller. - The admin sees it as unregistered. The consent screen marks it Not registered with Cypress, says it registered itself and where the browser returns; Settings → Connected apps shows where it returns too.
The flow
1. Send the admin to Cypress.
GET /api/oauth/authorize
?response_type=code
&client_id=cyp_app_…
&redirect_uri=https://your.app/callback
&scope=offers:write webhooks
&agents=sell
&state=<your value>
&code_challenge=<base64url(SHA-256(verifier))>
&code_challenge_method=S256PKCE with S256 is required. agents is a comma list of your agent keys and
may be left out. Every scope an agent has must also be in scope.
2. The admin approves. Cypress shows the app, the organization, what the app will be able to do and the agents it will add. An admin of the organization approves, or Cypress staff acting for it (on its behalf, and the audit log says so). A connection staff approve creates the agents, but your person token acts for nobody, since staff are not members of the organization: an app that must act as a person is connected by one of its people. Approving again later updates the same connection.
3. Cypress sends the admin back to your redirect address with code and
your state. A refusal comes back as error=access_denied. A code works once,
for 10 minutes.
4. Exchange the code.
curl -X POST https://app.getcypress.ai/api/oauth/token \
-u "cyp_app_…:cyp_cs_…" \
-d grant_type=authorization_code \
-d code=cyp_ac_… \
-d redirect_uri=https://your.app/callback \
-d code_verifier=<the verifier>{
"access_token": "cyp_at_…",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "cyp_rt_…",
"scope": "offers:write webhooks",
"connection_id": "…",
"organization_id": "…",
"agents": [{ "id": "…", "key": "sell", "name": "Cedar Sell", "scopes": ["offers:write"] }]
}This token acts for the admin who approved, while they remain a member of the organization.
5. Get a token for an agent. Agent tokens are never handed out in step 4. Ask for one with your secret and the agent's id:
curl -X POST https://app.getcypress.ai/api/oauth/token \
-u "cyp_app_…:cyp_cs_…" \
-d grant_type=client_credentials \
-d agent_id=<agent id>The token is bound to that agent and lasts an hour. There is no refresh token: ask again.
6. Refresh. grant_type=refresh_token returns a new access token and a new
refresh token; the old refresh token stops working. Presenting a used refresh
token again is treated as a leak: every token for that person on that
connection is revoked.
7. Revoke a token you no longer need at POST /api/oauth/revoke with
token=… and your client credentials.
Calling the API with a token
Send Authorization: Bearer cyp_at_…. A token works on the app's actions,
the MCP servers and most of the API, under the scope rules for API keys,
applied to the scopes the organization approved. A few routes belong to the
system that runs an integration and take an API key only (webhook
subscriptions, the delivery log, disconnecting an app, and pushing vehicle
data): they answer a token 401, "This endpoint accepts API keys only; app
and agent tokens are not accepted here". The list is in
Keys and tokens.
A token stops working at once when the connection is revoked, when the person it acts for leaves the organization, or when the agent is switched off.
What agents may not do
An agent works under the app's rules with a fleet manager's authority, and
some decisions are never its own: accepting or countering below the floor,
setting the floor, emailing a contact, sending a campaign, approving a
quarter, confirming that money arrived, and switching an automation back on.
Each answers 403 PEOPLE_ONLY (on Cypress Operate, a decision with a link for
a person). An agent never signs a document, accepts terms, changes billing or
manages the team; those endpoints do not accept agent tokens at all. Area by
area, with what the agent is told: What an agent can and cannot do.
Disconnecting
The organization's admin disconnects your app in Settings › Connected apps.
An integration of the organization can do it too, with an API key holding
connections:write: DELETE /api/v1/connections/{id}. Either way every token
ends at once and every agent your app added is switched off. Its actions stay
in the organization's activity log.
Sign in with Cypress
To know who is using your app, call GET /api/oauth/userinfo with the app's
token:
{
"sub": "…",
"kind": "PERSON",
"name": "Ana Ruiz",
"email": "ana@fleet.example",
"role": "ADMIN",
"organization": { "id": "…", "name": "Fox Fleet" },
"scope": "offers:write webhooks"
}With an agent's token it answers kind: AGENT, the agent's name and its
organization, and no address.
For a full OpenID Connect sign-in, where your app signs people in with their Cypress login and receives an ID token, ask Cypress to register your app with Cypress's identity provider as well. That registration is separate from the one above.
Sandbox
A sandbox organization to build your integration against, with vehicles, offers, a closing, a CRM book and a buy plan already in it, a key with every scope, and a reset your agent can call.
The app's actions
Every button of the app's screens as a REST operation and a Cypress Operate tool that run the app's own code as you, with the webhook each change sends.