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

# Authentication

> Give the client your API credentials once; it gets, caches and renews the access token.

Every call to the API must include an access token. The SDKs get that token for you from your API
credentials, cache it and renew it before it expires. With cURL, or any HTTP client, you request the
token yourself and send it with each call.

## Your credentials

| Credential | What it is |
| - | - |
| Base URL | Your Exsited API address, including `https://`. |
| Client ID | Identifies your integration. |
| Client secret | The secret paired with the client ID. |
| Redirect URI | The redirect URI registered for your integration, sent with the token request. |

> [!WARNING]
>
> The client secret gives access to your Exsited data. Keep it in an environment variable or a
> secrets manager, never in source code.

## Set up a client

Register your API credentials once, when your app starts. These samples read them from environment
variables named `EXSITED_BASE_URL`, `EXSITED_CLIENT_ID`, `EXSITED_CLIENT_SECRET` and
`EXSITED_REDIRECT_URI`.

```bash cURL theme={null}
curl -X POST "$EXSITED_BASE_URL/api/v4/oauth2/token" \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "'"$EXSITED_CLIENT_ID"'",
    "client_secret": "'"$EXSITED_CLIENT_SECRET"'",
    "redirect_uri": "'"$EXSITED_REDIRECT_URI"'"
  }'
```

```python Python theme={null}
import os

from exsited import AuthConfig, SdkConfig, register_instances

register_instances([
    SdkConfig(
        AuthConfig(
            client_name="default",
            base_url=os.environ["EXSITED_BASE_URL"],
            client_id=os.environ["EXSITED_CLIENT_ID"],
            client_secret=os.environ["EXSITED_CLIENT_SECRET"],
            redirect_uri=os.environ["EXSITED_REDIRECT_URI"],
        ),
        raise_on_error=True,
    ),
])
```

```javascript JavaScript theme={null}
import {AuthConfig, SdkConfig, registerInstances} from 'exsited';

registerInstances([
    new SdkConfig(
        new AuthConfig({
            clientName: 'default',
            baseUrl: process.env.EXSITED_BASE_URL,
            clientId: process.env.EXSITED_CLIENT_ID,
            clientSecret: process.env.EXSITED_CLIENT_SECRET,
            redirectUri: process.env.EXSITED_REDIRECT_URI,
        }),
        {raiseOnError: true},
    ),
]);
```

```php PHP theme={null}
<?php

require __DIR__ . '/vendor/autoload.php';

use Exsited\Client\ClientRegistry;
use Exsited\Config\AuthConfig;
use Exsited\Config\SdkConfig;

ClientRegistry::registerInstances([
    new SdkConfig(
        new AuthConfig(
            clientName: 'default',
            baseUrl: getenv('EXSITED_BASE_URL'),
            clientId: getenv('EXSITED_CLIENT_ID'),
            clientSecret: getenv('EXSITED_CLIENT_SECRET'),
            redirectUri: getenv('EXSITED_REDIRECT_URI'),
        ),
        raiseOnError: true,
    ),
]);
```

```java Java theme={null}
import com.exsited.client.ClientRegistry;
import com.exsited.config.AuthConfig;
import com.exsited.config.SdkConfig;
import java.util.List;

public class Main {
    public static void main(String[] args) throws Exception {
        ClientRegistry.registerInstances(List.of(
                SdkConfig.builder(AuthConfig.builder()
                        .clientName("default")
                        .baseUrl(System.getenv("EXSITED_BASE_URL"))
                        .clientId(System.getenv("EXSITED_CLIENT_ID"))
                        .clientSecret(System.getenv("EXSITED_CLIENT_SECRET"))
                        .redirectUri(System.getenv("EXSITED_REDIRECT_URI"))
                        .build())
                        .raiseOnError(true)
                        .build()));
    }
}
```

```csharp .NET theme={null}
using Exsited.Client;
using Exsited.Config;

ClientRegistry.RegisterInstances(new object[]
{
    new SdkConfig(
        new AuthConfig(
            clientName: "default",
            baseUrl: Environment.GetEnvironmentVariable("EXSITED_BASE_URL") ?? "",
            clientId: Environment.GetEnvironmentVariable("EXSITED_CLIENT_ID") ?? "",
            clientSecret: Environment.GetEnvironmentVariable("EXSITED_CLIENT_SECRET") ?? "",
            redirectUri: Environment.GetEnvironmentVariable("EXSITED_REDIRECT_URI") ?? ""),
        raiseOnError: true),
});
```

```dart Dart theme={null}
import 'dart:io';

import 'package:exsited/exsited.dart';

Future<void> main() async {
  registerInstances([
    SdkConfig(
      AuthConfig(
        clientName: 'default',
        baseUrl: Platform.environment['EXSITED_BASE_URL']!,
        clientId: Platform.environment['EXSITED_CLIENT_ID']!,
        clientSecret: Platform.environment['EXSITED_CLIENT_SECRET']!,
        redirectUri: Platform.environment['EXSITED_REDIRECT_URI']!,
      ),
      raiseOnError: true,
    ),
  ]);
}
```

```go Go theme={null}
package main

import (
	"log"
	"os"

	exsited "github.com/exsited/exsited-sdk-go/v4"
)

func main() {
	err := exsited.RegisterInstances(exsited.SdkConfig{
		Auth: exsited.AuthConfig{
			ClientName:   "default",
			BaseURL:      os.Getenv("EXSITED_BASE_URL"),
			ClientID:     os.Getenv("EXSITED_CLIENT_ID"),
			ClientSecret: os.Getenv("EXSITED_CLIENT_SECRET"),
			RedirectURI:  os.Getenv("EXSITED_REDIRECT_URI"),
		},
		RaiseOnError: true,
	})
	if err != nil {
		log.Fatal(err)
	}
}
```

Then get a client wherever you make calls. Every SDK sample in the API reference starts this way:

```python Python theme={null}
from exsited import build_client

client = build_client()
```

```javascript JavaScript theme={null}
import {buildClient} from 'exsited';

const client = await buildClient();
```

```php PHP theme={null}
use Exsited\Client\ClientRegistry;

$client = ClientRegistry::buildClient();
```

```java Java theme={null}
import com.exsited.client.ClientRegistry;
import com.exsited.client.ExsitedClient;

ExsitedClient client = ClientRegistry.buildClient();
```

```csharp .NET theme={null}
using Exsited.Client;

await using var client = ExsitedClient.BuildClient();
```

```dart Dart theme={null}
import 'package:exsited/exsited.dart';

final client = buildClient();
```

```go Go theme={null}
import (
	"log"

	exsited "github.com/exsited/exsited-sdk-go/v4"
)

client, err := exsited.BuildClient(nil)
if err != nil {
	log.Fatal(err)
}
defer client.Close()
```

The registration turns on the raise-on-error option (`raise_on_error=True` in Python) for every
client you build, so a failed call raises an exception or throws an error instead of going
unnoticed. Without it, a failed call returns a response that holds the API's error body. A `2xx`
response whose body is an error also counts as a failure. In Go, a `nil` query argument sends no
query parameters, and a `nil` raise-on-error argument uses the setting from your registration.

## What the SDKs handle

> [card] Getting the token
>
> On the first call, the SDK exchanges your credentials for an access token.

> [card] Caching and renewal
>
> The SDK caches the token and renews it shortly before it expires, trying the refresh token first
> and then your credentials.

> [card] An expired token
>
> If a call returns `401` because the token has expired, the SDK renews the token once and sends
> the call again.

> [card] A real refusal
>
> A `401` that refuses the request itself, rather than the token, is returned to you and not
> retried, so a write is never sent twice.

> [!NOTE]
>
> A new token for the same credentials ends the previous one. Two apps, or two copies of one app,
> that each request their own token with the same credentials keep signing each other out. Share
> one set of credentials only with one running app, or give each app its own.

## Calling the API directly

Request a token from `POST /api/v4/oauth2/token` with a JSON body of `grant_type`
(`client_credentials`), `client_id`, `client_secret` and `redirect_uri`. The cURL tab under
[Set up a client](#set-up-a-client) shows the full request. A successful request returns `200` with
these fields:

| Field | Meaning |
| - | - |
| `access_token` | The token to send with each call. |
| `expires_in` | Seconds until the token expires, for example `3600`. |
| `refresh_token` | Exchanged for a new access token when this one expires. |

Send the access token in the `Authorization` header on every call:

```bash theme={null}
curl "$EXSITED_BASE_URL/api/v4/accounts?limit=20" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

To renew a token, send `grant_type` `refresh_token` with `client_id`, `client_secret` and the
`refresh_token` from the earlier response.

## Errors

Every failed call returns the same JSON body:

```json theme={null}
{
  "object": "error",
  "request_id": "req_23019cc8-cdce-42db-ae8f-b3fdb1e15493",
  "status": 400,
  "code": "validation_error",
  "message": "One or more fields failed validation",
  "errors": [
    {"field": "effective_date", "code": "invalid", "message": "Invalid format for effective_date"}
  ]
}
```

Branch on `code`, and quote `request_id` when you contact support. `errors` has one entry per field
that failed.

## Try it in the docs

Every endpoint page has a **Try it** button that calls the API from this site.

**1. Get a token**

Open [Get an access token](https://exsited.mintlify.site/api-reference/authentication/get-an-access-token.md), click **Try it**,
set `host` to your Exsited API host name (without `https://`), choose `client_credentials` for
`grant_type`, fill in your credentials and click **Send**. Copy `access_token` from the response.

**2. Call an endpoint**

Open any endpoint, click **Try it**, set the same `host`, paste the token into
**Authorization**, and click **Send**.

> [!WARNING]
>
> These calls run against your real data: creating, changing and deleting records is real. Requests
> are sent through Mintlify's servers. Use test data, and never share your credentials.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.