# FAQ & Troubleshooting

Answers to the questions integrators most commonly hit against the Titan Public API.

## Auth & login

### How do I get a token?

The API uses **OAuth 2.0 client credentials**. Create a `CLIENT_ID` / `CLIENT_SECRET` pair from **Connectors → APIs** in the SecurityScorecard app, then exchange them at `POST /v1/oauth/token` with `grant_type=client_credentials`. Full walkthrough in [Getting Started](/docs/guides/getting-started).

The same credentials work for both REST and [MCP](/docs/guides/mcp).

### I'm getting `401 Unauthorized`. Why?

`401` means the request was **not authenticated**. Common causes:

- No `Authorization: Bearer …` header on the request.
- The bearer token is malformed (extra whitespace, missing `Bearer ` prefix, or the header holds the raw `CLIENT_SECRET` instead of an access token).
- The access token has expired — re-exchange your `CLIENT_ID` / `CLIENT_SECRET`.
- The credential has been deleted or rotated. Confirm it still exists on the **Connectors → APIs** page.


### I'm getting `403 Forbidden`. Why?

`403` means the token was **valid** but the caller is **not permitted**. Common causes:

- The credential lacks the required scope. Read-only calls need **Read security findings**; mutating calls additionally need **Flag security findings**. Scopes are set when the credential is created — issue a new credential with the right scopes.
- The token's `org` claim doesn't match the resource. Every credential is scoped to the organization that issued it.


## Rate limits

### I'm getting `429 Too Many Requests`. What do I do?

Honour the `Retry-After` header — it tells you how many seconds to wait before retrying. If unavailable, fall back to **exponential backoff with jitter** (base 1s, factor 2, cap 60s).

Do **not** retry immediately, and do **not** rotate credentials or IPs to evade the limit — the counter is keyed by **organization**. See [Usage Limits](/docs/guides/usage-limits).

### Do MCP and REST share a rate-limit budget?

The limits are **numerically identical** but the counters are **independent** — a burst on REST won't starve MCP or vice versa.

### How do I get higher limits?

Contact your SecurityScorecard account team with your organization identifier, the endpoints you'll call, and whether traffic is steady or bursty. See [Usage Limits](/docs/guides/usage-limits#getting-more-headroom).

## Idempotency

### I retried a flag action and got a conflict. What does that mean?

Flag actions are **idempotent by design**. A `409` conflict means the resource is in a state that rejects the operation — for example, flagging something already resolved. Do not blindly retry a `409`. Read `error.code` and either adjust the request or treat the current state as the accepted outcome. See [Errors](/docs/guides/errors#status-codes).

## MCP connector setup

### I added the Titan MCP connector but the client doesn't call it.

Interactive AI clients don't always auto-invoke MCP tools from a generic prompt. Reliable fixes:

1. Name the tool explicitly: *"Use `GetObservations` to list this week's CVE findings."*
2. Name the connector: *"Using the Titan MCP connector, show me high-severity findings."*
3. Ask the client to list tools first: *"List the tools available from the Titan MCP server, then answer …"*


See [MCP → Prompting](/docs/guides/mcp#prompting-getting-the-client-to-call-titan-mcp).

### The MCP endpoint returns `401`.

Same causes as REST `401` above. MCP uses the **same bearer token** as REST.

### The MCP endpoint returns `403`.

The credential lacks the scope for the tool you invoked. See [MCP → Scopes](/docs/guides/mcp#scopes).

### The connector connects but returns no data.

- The credential's `org` must match the organization whose data you expect — cross-org calls always return empty.
- Confirm the tool actually ran by asking the client to show its tool-call trace.


## Pagination

### How do I know I've reached the end?

**Cursor mode:** `next_cursor` is absent or empty. A short page is **not** a reliable end-of-stream signal — always check `next_cursor`.

**Offset mode:** the response contains zero rows.

See [Pagination](/docs/guides/pagination).

### The cursor stopped working after a while.

Cursors expire after ~24 hours. Restart from `cursor=""` and dedupe by resource id.

### Can I parallelise a scan by splitting the cursor across workers?

No — give each worker a **disjoint filter** (e.g. a distinct time range) instead. See [Pagination](/docs/guides/pagination#guidance).

## Reporting a bug

Include **`error.request_id`** from the failing response — without it we cannot correlate your call to server logs. Full checklist in [Errors → Reporting a bug](/docs/guides/errors#reporting-a-bug).