Connect a client
Connect Claude, ChatGPT, Cursor or any MCP client to Dashly with OAuth or an API key.
The server URL is the same for every client:
https://mcp.usedashly.comThere are two ways to authenticate, and both arrive the same way, as an
Authorization: Bearer … header:
| Credential | For | How you get it |
|---|---|---|
| OAuth token | A member connecting an assistant (Claude, ChatGPT, Cursor…) | The client runs the OAuth flow itself; the member signs in and approves |
API key ak_… | Server-to-server integrations, scripts, clients that take a header | Created in the dashboard under Connectors → API & webhooks |
Most cloud assistants cannot ask you to paste a header, so they use OAuth. Everything below the credential is identical: same tools, same scopes, same limits.
Claude
In Claude, add a custom connector and give it the URL https://mcp.usedashly.com.
No client id or secret is needed: Claude registers itself.
Click Connect. Claude opens Dashly in your browser. Sign in if you are not already.
Review the consent screen, which lists what the connector will be able to do, and click Authorize.
In Claude Code, add it as an HTTP server. With OAuth, run /mcp afterwards to sign in;
with a key, pass the header:
claude mcp add --transport http dashly https://mcp.usedashly.com \
--header "Authorization: Bearer $DASHLY_API_KEY"ChatGPT
Add Dashly as a connector (an MCP server) with the URL https://mcp.usedashly.com and
OAuth as the authentication method. ChatGPT can list the tools before you sign in; it asks
you to connect the first time it needs Dashly data, then runs the same sign-in and consent
as Claude.
Cursor and other clients
A client that supports remote HTTP servers with OAuth only needs the URL. A client that
takes headers can use an API key instead. In Cursor (.cursor/mcp.json):
{
"mcpServers": {
"dashly": {
"url": "https://mcp.usedashly.com",
"headers": { "Authorization": "Bearer ak_…" }
}
}
}An API key in a config file is a secret. Do not commit it, and prefer OAuth where the client supports it: an OAuth connection is tied to a person and can be revoked on its own.
The OAuth flow
Dashly implements the MCP authorization flow, so a compliant client discovers everything from the first refused request.
Challenge. A request that needs a credential and has none is answered 401 with:
WWW-Authenticate: Bearer realm="Dashly", error="invalid_token", error_description="Authorization required", resource_metadata="https://mcp.usedashly.com/.well-known/oauth-protected-resource"Inside a tools/call, the same challenge travels in the tool result's
_meta["mcp/www_authenticate"].
Protected-resource metadata (RFC 9728) at
https://mcp.usedashly.com/.well-known/oauth-protected-resource names the
authorization server:
{
"resource": "https://mcp.usedashly.com",
"authorization_servers": ["https://app.usedashly.com"],
"bearer_methods_supported": ["header"],
"scopes_supported": ["openid", "profile", "email", "offline_access"]
}Authorization-server metadata (RFC 8414) at
https://app.usedashly.com/.well-known/oauth-authorization-server lists the
authorization, token and registration endpoints. Read them from there rather than
hard-coding them. The authorization endpoint is https://app.usedashly.com/oauth/authorize.
Dynamic client registration (RFC 7591). The client registers itself at the
registration endpoint and receives a client_id. Nobody has to pre-register anything.
Sign-in and consent. The member signs in to Dashly and sees an explicit consent screen. The redirect URI must be one the client registered. PKCE is required.
Token. The client exchanges the code for an access token (and a refresh token with
offline_access) and sends it as Authorization: Bearer … on every request.
What a connector may do
A connector has the permissions of the member who authorized it, and nothing more. The
OAuth scopes the client asks for (openid profile email) say nothing about agency data;
what the connector can read and write is computed from the member's role in the agency and
shown on the consent screen before they approve.
- The permissions are a snapshot taken when the connection is made. If the member's role changes later, the connector keeps its original rights until it is authorized again.
- The connector acts for the member's agency. A member who belongs to several agencies connects to the one they joined first.
- A member who is removed from the agency takes their connectors with them.
A grant becomes an API key
Behind every OAuth connection, Dashly creates an API key on the connector's first call. It is labelled as an OAuth connector under Connectors → API & webhooks, next to the keys created by hand, and it behaves like any other key:
- Same limits. Its calls draw on the agency's monthly token allowance and burst window. See Rate limits.
- Revocable. Revoking it cuts the connector off immediately, not when its current token happens to expire. Presenting an old token afterwards does not bring it back: only a fresh authorization does.
- One live connection per app. Reconnecting the same application (same name, same redirect address) revokes the member's older connection for it, so the list never holds two live "Claude" for one person. Other members' connections, other applications and hand-made keys are untouched.
Checking a connection
Call get_account (or read dashly://me) to see which agency the credential belongs to,
its scopes, and how much of the monthly allowance is left. When a credential has been revoked
or has expired, initialize answers 401 with the challenge above, which makes a compliant
client run the OAuth flow again.