> ## Documentation Index
> Fetch the complete documentation index at: https://developers.novatrade24.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> OAuth2 client_credentials via Keycloak service accounts. No per-user login, no passwords in your backend.

The Integration API uses **OAuth2 client\_credentials** for machine-to-machine
authentication. Keycloak service accounts (realm `nt24-idp`) issue short-lived
access tokens bound to your `IntegrationOrganization`. There is no per-user
login flow — every API call is authenticated by a bearer token minted from your
client credentials.

## Why client\_credentials

* **No user in the loop.** API calls are server-to-server; no browser
  redirects or login prompts.
* **Short-lived tokens.** Access tokens expire quickly (typically 5 minutes).
  Leaked tokens auto-expire.
* **Rotatable secrets.** Your `client_secret` rotates via Keycloak without
  changing any integration code.
* **Standard tooling.** Works with every OAuth2-compatible HTTP client (Spring,
  Axios, httpx, etc.).

## Token endpoint

```
POST https://auth.novatrade24.com/realms/nt24-idp/protocol/openid-connect/token
```

Request body — `application/x-www-form-urlencoded`:

```
grant_type=client_credentials
&client_id=api-client-{your-slug}
&client_secret={secret}
```

Response:

```json theme={null}
{
  "access_token": "eyJhbGc...",
  "expires_in": 300,
  "token_type": "Bearer",
  "not-before-policy": 0,
  "scope": "profile email"
}
```

Use `access_token` in every API request as `Authorization: Bearer <token>`.

## Token caching

Tokens are valid for `expires_in` seconds (typically 300). **Cache the token**
and refresh a minute before expiry.

<CodeGroup>
  ```typescript TypeScript theme={null}
  class TokenProvider {
    private token: string | null = null;
    private expiresAt = 0;

    async getToken(): Promise<string> {
      const now = Date.now();
      if (this.token && now < this.expiresAt - 60_000) return this.token;

      const r = await fetch(
        'https://auth.novatrade24.com/realms/nt24-idp/protocol/openid-connect/token',
        {
          method: 'POST',
          headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
          body: new URLSearchParams({
            grant_type: 'client_credentials',
            client_id: process.env.NT24_CLIENT_ID!,
            client_secret: process.env.NT24_CLIENT_SECRET!,
          }),
        }
      );
      const { access_token, expires_in } = await r.json();
      this.token = access_token;
      this.expiresAt = now + expires_in * 1000;
      return this.token!;
    }
  }
  ```

  ```java Java theme={null}
  // Use Spring Security's OAuth2 client_credentials flow:
  @Bean
  OAuth2AuthorizedClientManager clientManager(
          ClientRegistrationRepository registrations,
          OAuth2AuthorizedClientService clientService) {

      var provider = OAuth2AuthorizedClientProviderBuilder.builder()
          .clientCredentials()
          .build();

      var manager = new AuthorizedClientServiceOAuth2AuthorizedClientManager(
          registrations, clientService);
      manager.setAuthorizedClientProvider(provider);
      return manager;
  }

  // Token is auto-refreshed by Spring.
  ```
</CodeGroup>

## Secret rotation

Rotate your `client_secret` via Keycloak admin API or by contacting support
for a regeneration. Process:

1. Request a new secret (both old and new valid during rotation window).
2. Update your backend to use the new secret.
3. Old secret is revoked after you confirm the cutover.

Rotate on a schedule (quarterly recommended) and immediately if you suspect
exposure. Rotation does not invalidate already-issued access tokens — only
new token requests fail with the old secret.

## Organization and partner scoping

Your Keycloak client is linked to exactly one `IntegrationOrganization`. The
organization defines:

* **Authorized partners** — the `TradePartner` UUIDs you can act on behalf of.
  Every endpoint that touches partner-scoped data requires the partner UUID
  in the URL path (`/partners/{sellerId}/...`).
* **Capability flags** — features gated on organization entitlement (e.g.
  `allowMarketplaceLedKyc`, `rateLimitTier`).
* **DPA acceptance** — Data Processing Agreement version and acceptance
  timestamp. Requests are rejected with `403` if the DPA is missing or outdated.

Discover both via `GET /v1/me` at startup.

## Troubleshooting

<AccordionGroup>
  <Accordion title="401 Unauthorized on API call">
    * Token expired — re-issue via the token endpoint.
    * Wrong realm — verify `realms/nt24-idp` in the token URL.
    * Malformed `Authorization` header — must be exactly `Bearer <token>` (single space).
  </Accordion>

  <Accordion title="403 Forbidden on API call">
    * Partner path (`{sellerId}`) not in your authorized partners list. Check `GET /v1/me`.
    * Missing capability for the endpoint (e.g. Mode A KYC requires `allowMarketplaceLedKyc=true`).
    * DPA not accepted for your organization.
  </Accordion>

  <Accordion title="`invalid_client` response from token endpoint">
    * Wrong `client_id` or `client_secret`.
    * Client disabled in Keycloak — contact support.
    * Service Account Roles disabled on the client configuration.
  </Accordion>

  <Accordion title="`invalid_grant` response from token endpoint">
    * `grant_type` must be exactly `client_credentials` (lowercase).
    * Do not include `username` or `password` — those are for a different grant flow.
  </Accordion>
</AccordionGroup>

## Next

<CardGroup cols={2}>
  <Card title="Quickstart" icon="bolt" href="/quickstart">
    Call your first endpoint.
  </Card>

  <Card title="Error reference" icon="triangle-exclamation" href="/reference/errors">
    Full RFC 7807 problem type catalog.
  </Card>
</CardGroup>
