# Getting credentials

> The three identities an operation can require, and how to present them. The API's contract says which ones each operation accepts (security). Product side…

Guide - Since version 0.3.0 - Flexway v1.6.3 - https://flexway.io/en/docs/developer-credentials/

> The three identities an operation can require, and how to present them. The API's contract says which ones each operation accepts (`security`).
>
> Product side: [Identity and zero trust](https://flexway.io/en/docs/product-identity/); operations side: [Wiring your identity provider](https://flexway.io/en/docs/operations-identity/), [API keys](https://flexway.io/en/docs/operations-api-keys/), [Client certificates (mTLS)](https://flexway.io/en/docs/operations-client-certificates/).

## Which credential for which case

| You are | Credential | Presented by |
| --- | --- | --- |
| An application acting for a person, or a service | An **OIDC token** from the operator's identity provider | `Authorization: Bearer <token>` |
| A known integration with no user (partner, batch) | An **API key** handed out by the operator | `Authorization: ApiKey <key>` |
| A partner server | A **client certificate** (mTLS) declared by the operator | The TLS handshake |
| An AI agent (MCP) | An OIDC token issued for the MCP surface | See [MCP](https://flexway.io/en/docs/developer-mcp/) |

Your **tenant** (the customer you act for) always comes from the credential: a claim of the token, the
key or the certificate. A tenant header you write never reaches the API.

## OIDC token

Ask the operator's identity provider for a token, for the API's **audience** and with the **scopes**
the contract requires. For a service with no user, the *client credentials* flow:

```bash
TOKEN=$(curl -s https://login.example.com/oauth2/token \
  -d grant_type=client_credentials -d client_id=billing -d client_secret="$SECRET" \
  -d scope="orders:read" -d audience=api | jq -r .access_token)
curl -H "Authorization: Bearer $TOKEN" https://api.example.com/orders/42
```

The exact audience parameter depends on the provider (`audience`, `resource`, a dedicated scope);
the operator gives you the provider's URL, the audience and the scopes.

## API key

| Rule | Detail |
| --- | --- |
| Format | `fwk_` followed by 43 characters; shown **once** when created |
| Header | `Authorization: ApiKey <key>` only - never `X-Api-Key`, never in the URL (ignored) |
| Scope | Bound to **one** tenant, to **named operations** and to an **end date** |
| Rotation | The operator issues a second key, you switch, the old one is removed |
| Rate | Counted per key |

```bash
curl -H "Authorization: ApiKey $FLEXWAY_KEY" https://api.example.com/invoices
```

## Client certificate

```bash
curl --cert client.crt --key client.key https://api.example.com/settlements -X POST -d @settle.json
```

The certificate is declared by the operator (preferably by its URI SAN, which survives a renewal)
and bound to a tenant and to operations.

## What you get

| Situation | Response |
| --- | --- |
| No credential | `401`, `WWW-Authenticate: Bearer` |
| Invalid, expired, wrong-issuer or wrong-audience token | `401`, `error="invalid_token"` - never the exact reason |
| Valid token without the scope | `403`, `error="insufficient_scope", scope="..."` |
| Unknown or expired key, or one without the `fwk_` prefix | `401`, `WWW-Authenticate: ApiKey` |
| Valid key on an operation it does not name | `403` |
| Undeclared certificate, or none | `401` |
| A token on an operation that accepts `mutualTLS` only | `401` |

A token never stands in for a key, nor the reverse: the contract says which alternative each
operation accepts.
