> This is Payabli documentation. For a complete page index, fetch https://docs.payabli.com/llms.txt — append .md to any page URL for lightweight markdown. For section-level indexes, query parameters, and other AI-optimized access methods, see https://docs.payabli.com/ai-agents.md

# OAuth authentication

> Learn how to authenticate to the Payabli API using the OAuth2 client-credentials flow

Payabli supports OAuth2 for authenticating API requests. Your integration provisions a **client ID** and **client secret**, exchanges them for a short-lived **Bearer access token**, and sends that token on subsequent API calls. This is a server-to-server flow: the credentials and the token stay on your backend.

OAuth2 is one of two authentication methods. The `requestToken` API token is documented separately in [API token authentication](/developers/api-tokens). The two coexist. You don't need to migrate existing `requestToken` integrations.

## How it works

The flow has three steps:

1. **Provision credentials** in the Payabli Portal. You receive a client ID and client secret, scoped to an environment.
2. **Request an access token** by exchanging your credentials at the token endpoint.
3. **Call the API** with the token in the `Authorization` header.

The access token is short-lived. When it expires, request a new one with the same credentials.

## Step 1: Provision credentials

Credentials are created and managed in the Payabli Portal. Create an **API Authentication** credential, which issues:

* A **client ID**, which identifies your integration.
* A **client secret**, which authenticates it.

When you create the credential, you set a **credential lifetime**: how long the client ID and secret stay valid before you must rotate them. You can set the lifetime to 30, 60, 90, or 120 days, or 1 year; 90 days is recommended. This is separate from the access token's lifetime. The token you request in Step 2 is short-lived; its exact lifetime is returned in the response's `expires_in` field (for example, one hour in sandbox) and can differ by environment. Read `expires_in` rather than assuming a fixed lifetime. The credential lifetime governs the client ID and secret themselves.

The permissions that determine which endpoints and actions the credential can access are also assigned at this stage. Credentials are **environment-scoped**: you get separate credentials for sandbox and for production.

The client secret is shown only once, at creation. Store it securely on your backend and never expose it in client-side code, URLs, or public repositories. If a secret is lost or compromised, rotate it in the portal.

For step-by-step instructions to create and manage credentials in the portal, see [Manage API credentials](/guides/platform-developer-api-credentials-manage).

## Step 2: Get an access token

Exchange your client ID and client secret for an access token at the token endpoint. The example below uses sandbox; in production, call `https://api.payabli.com/api/v2/Token/serverside`.

### Request

POST [https://api-sandbox.payabli.com/api/v2/Token/serverside](https://api-sandbox.payabli.com/api/v2/Token/serverside)

```curl
curl -X POST https://api-sandbox.payabli.com/api/v2/Token/serverside \
     -H "Content-Type: application/json" \
     -d '{
  "clientId": "YOUR_CLIENT_ID",
  "clientSecret": "YOUR_CLIENT_SECRET"
}'
```

```typescript
import { PayabliClient } from "@payabli/sdk-node";

async function main() {
    const client = new PayabliClient();
    await client.token.createServerSideToken({
        clientId: "YOUR_CLIENT_ID",
        clientSecret: "YOUR_CLIENT_SECRET",
    });
}
main();

```

```python
from payabli import payabli

client = payabli()

client.token.create_server_side_token(
    client_id="YOUR_CLIENT_ID",
    client_secret="YOUR_CLIENT_SECRET",
)

```

```java
package com.example.usage;

import io.github.payabli.api.PayabliPayabliApiOasClient;
import io.github.payabli.api.resources.token.requests.CreateServerSideTokenRequest;

public class Example {
    public static void main(String[] args) {
        PayabliPayabliApiOasClient client = PayabliPayabliApiOasClient
            .builder()
            .build();

        client.token().createServerSideToken(
            CreateServerSideTokenRequest
                .builder()
                .clientId("YOUR_CLIENT_ID")
                .clientSecret("YOUR_CLIENT_SECRET")
                .build()
        );
    }
}
```

```csharp
using PayabliPayabliApiOas;
using System.Threading.Tasks;

namespace Usage;

public class Example
{
    public async Task Do() {
        var client = new PayabliPayabliApiOasClient();

        await client.Token.CreateServerSideTokenAsync(
            new CreateServerSideTokenRequest {
                ClientId = "YOUR_CLIENT_ID",
                ClientSecret = "YOUR_CLIENT_SECRET"
            }
        );
    }

}

```

```go
package example

import (
    context "context"

    payabli "github.com/payabli/sdk-go"
    client "github.com/payabli/sdk-go/client"
)

func do() {
    client := client.NewClient()
    request := &payabli.CreateServerSideTokenRequest{
        ClientId: "YOUR_CLIENT_ID",
        ClientSecret: "YOUR_CLIENT_SECRET",
    }
    client.Token.CreateServerSideToken(
        context.TODO(),
        request,
    )
}

```

```php
<?php

namespace Example;

use Payabli\PayabliClient;
use Payabli\Token\Requests\CreateServerSideTokenRequest;

$client = new PayabliClient();
$client->token->createServerSideToken(
    new CreateServerSideTokenRequest([
        'clientId' => 'YOUR_CLIENT_ID',
        'clientSecret' => 'YOUR_CLIENT_SECRET',
    ]),
);

```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api-sandbox.payabli.com/api/v2/Token/serverside")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n  \"clientId\": \"YOUR_CLIENT_ID\",\n  \"clientSecret\": \"YOUR_CLIENT_SECRET\"\n}"

response = http.request(request)
puts response.read_body
```

```swift
import Foundation

let headers = ["Content-Type": "application/json"]
let parameters = [
  "clientId": "YOUR_CLIENT_ID",
  "clientSecret": "YOUR_CLIENT_SECRET"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api-sandbox.payabli.com/api/v2/Token/serverside")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

A successful response returns the token and its lifetime in seconds:

### Response (200)

```json
{
  "token_type": "Bearer",
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.example.token",
  "expires_in": 3600
}
```

If the credentials are invalid, the endpoint returns `400` with an error body:

```json
{
  "errorType": "InvalidCredentials",
  "errorMessage": "Invalid client credentials."
}
```

## Step 3: Make authenticated API calls

Send the access token as a Bearer token in the `Authorization` header on every request:

```bash
curl https://api-sandbox.payabli.com/api/v2/... \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

When a token expires, the API responds with `401 Unauthorized`. Request a new token and retry the call.

## Scopes and permissions

A credential's permissions control which endpoints and actions a token can access. By default, a token carries all the permissions granted to the credential that requested it.

To request a token scoped to a subset of those permissions, pass a `permissions` array of permission IDs in the token request:

```json
{
  "clientId": "YOUR_CLIENT_ID",
  "clientSecret": "YOUR_CLIENT_SECRET",
  "permissions": ["00000000-0000-0000-0000-000000000000"]
}
```

## Rotate and revoke credentials

Rotate a client secret or revoke a credential in the Payabli Portal. Rotate before the credential lifetime expires, or any time a secret may be compromised. Rotating issues a new secret and invalidates the old one; revoking disables the credential entirely. Tokens already issued remain valid until they expire, so rotate or revoke and then re-authenticate to pick up the change.

See [Refresh credentials](/guides/platform-developer-api-credentials-manage#refresh-credentials) to rotate a secret and [Delete credentials](/guides/platform-developer-api-credentials-manage#delete-credentials) to revoke access.