Modular Finance Dataflow

Machine credentials

A machine credential authenticates a program rather than a person. Use one when your own code talks to the MCP server: a scheduled job, a backend service, or an agent runtime you host yourself. There is no browser login and no consent screen - your service exchanges a client_id and client_secret for an access token whenever it needs one.

If instead a person is connecting Claude, ChatGPT or another MCP host, use the browser flow described in Connecting. Machine credentials are not for that, and MCP hosts cannot use them.

Getting a credential

Machine credentials are issued per contract by your account manager. You receive a client_id and a client_secret; the secret is shown once, at creation, and cannot be recovered afterwards - store it in whatever your service already uses for secrets.

Ask for one credential per system rather than one per person. Usage is attributed per credential, so separate credentials for, say, your nightly job and your production API let you see the two apart in your usage breakdown, cap them separately, and revoke one without disturbing the other.

Requesting a token

Exchange the credential at the token endpoint with HTTP Basic authentication:

POST https://dataflow.modularfinance.com/oauth/token
ParameterValue
grant_typeclient_credentials
resourcehttps://dataflow.modularfinance.com/mcp
scopeOptional. Defaults to everything your credential allows.
curl -sS -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=client_credentials \
  -d resource=https://dataflow.modularfinance.com/mcp \
  https://dataflow.modularfinance.com/oauth/token

The response is a standard OAuth token response:

{
  "access_token": "eyJhbGciOiJFZERTQSIsImtpZCI6…",
  "token_type": "Bearer",
  "expires_in": 900,
  "scope": "dataflow:entities.search dataflow:ownership.read",
  "resource": "https://dataflow.modularfinance.com/mcp"
}

resource is required. It identifies the MCP server the token is for (RFC 8707) and must match https://dataflow.modularfinance.com/mcp exactly. Most OAuth client libraries do not send it unless told to; if you get invalid_target, this is why.

Using the token

Send the token as a bearer on the MCP endpoint. Any MCP client library works - point it at the endpoint and give it a static bearer token instead of an OAuth flow. Over plain HTTP the call looks like this:

curl -sS https://dataflow.modularfinance.com/mcp \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
       "params":{"name":"search_securities","arguments":{"query":"Volvo"}}}'

Do not send an Origin header. The MCP endpoint accepts it only from browser origins we have allowlisted, and rejects anything else with 403 origin not allowed. Server-side HTTP clients do not set it; some browser and edge runtimes do.

Token lifetime and rate limits

Tokens last 15 minutes and there is no refresh token: when one expires, request another the same way. Cache the token for its lifetime and re-request on expiry or on a 401.

  • The token endpoint accepts 60 requests per minute per IP. A service that fetches a fresh token for every tool call will hit this.
  • The MCP endpoint accepts 120 requests per minute per credential.

Both return 429 when exceeded. Back off and retry.

Scopes

Without a scope parameter you get everything the credential is allowed and your contract entitles you to. Pass scope to request less - useful when a particular service should only reach part of the data:

scope=dataflow:ownership.read dataflow:short.read

Granted scopes are always intersected with your organization's entitlement at the moment the token is minted, so the scope in the response is what you actually hold. If a scope is added to or removed from your contract, the next token reflects it - nothing to reconnect. Asking only for scopes your organization does not have returns invalid_scope. See OAuth & scopes for the full list.

Rotating the secret

Ask your account manager to rotate. You receive the new secret immediately, and the secret it replaces keeps working for a 7-day overlap so you can deploy without an outage. After the overlap it stops.

If a secret has leaked, say so when you ask. The old secret can be retired the moment the new one is issued, and a single compromised secret can be revoked on its own - your credential keeps working on the replacement, and the client_id does not change.

Revoking

Any of these can be arranged through your account manager, from narrowest to broadest:

ActionEffect
Revoke one secretThat secret stops authenticating. The credential keeps working on its others.
Revoke one tokenA specific issued access token stops working immediately.
Revoke the credentialNo new tokens are issued. Tokens already issued keep working until they expire, at most 15 minutes.

Errors

ErrorMeaning
invalid_clientWrong client_id or client_secret, a revoked credential, or the wrong authentication method - Basic and form-body are fixed per credential, and only the registered one is accepted.
invalid_targetresource is missing or does not match the MCP endpoint exactly.
invalid_scopeThe requested scope is outside what your credential or your contract allows.
unauthorized_clientMCP is not enabled for your organization, or the credential is not a machine credential.
401 with invalid_token on /mcpThe token expired or was revoked. Request a new one.
forbidden_scopeThe token does not carry the scope that tool requires.
quota_exceededThe monthly credit cap is reached; see Credits & pricing.
403 origin not allowedYour client sent an Origin header. Remove it.