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

# API concepts

> Set up your environment, configure branding, and authenticate your first API request with Tesouro.

Work with Tesouro's Implementation team to get set up with access to our environment and configure your branding.

## Authentication

<TestId testId="connect" />

The first steps for integrating with Tesouro are to:

1. Activate your developer account
2. Receive your API access key credentials
3. Create and authenticate your first JSON Web Token (JWT) to Tesouro.

### Access your organization's API access key credentials

Your organization's API access key and secret are created by Tesouro and shared with you. To receive access:

1. Have your designated team lead submit your access request [here](https://d92rm.share.hsforms.com/2rxMT-N-nQ-atFj2t5ODm2w) *(external site managed by Tesouro)*

2. Tesouro will email you an API access key and secret that will require passwordless authentication via email address.

### Create and authenticate your JWT

Create a JWT, then add it to the header of your call to query the
Tesouro Sandbox environment

```bash title="Authentication Request" lines theme={null}
  curl --location 'https://api.sandbox.tesouro.com/openid/connect/token' \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --data-urlencode 'grant_type=client_credentials' \
    --data-urlencode 'client_id=CLIENT_ID' \
    --data-urlencode 'client_secret=CLIENT_SECRET'
```

```json title="Authentication Request" lines theme={null}
{
  "access_token": "efJhbGcifiJv4zI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600
}
```

```bash title="Example Request" lines expandable theme={null}
curl -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ADD_TOKEN_HERE" \
  -d '{
      "query": "query bankAccounts($input: BankAccountQueryInput!) {
          bankAccounts(input: $input) {
            items {
              bankAccountBalance
              id
              bankAccountNickname
              bankAccountNumber
              routingNumber
            }
          }
        }",
      "variables": {
        "input": {
          "where": {"id": {"eq": "eaae8b39-e86b-4cce-9fa3-68f633c08ab6"}},
          "paging": {
            "skip": 0,
            "take": 10
          }
        }
      }
    }' \
  https://api.sandbox.business-banking.app/api/embedded
```

```json title="Example Response" lines theme={null}
{
  "data": {
    "organization": {
      "id": "66f41f51-cc83-440b-948f-afe3d4c57406",
      "businessName": "Tesouro Docs Sandbox"
    }
  }
}
```

<Info>
  For **production**, use **`https://api.business-banking.app`** instead of **`https://api.sandbox.business-banking.app`** with production credentials
</Info>

### Token Exchange for User Impersonation

For seamless widget and API experiences, Tesouro supports OAuth 2.0 Token Exchange (RFC 8693). This allows your application to impersonate users on the Tesouro platform by exchanging a user JWT for an access token.

```bash title="Token Exchange Request" lines theme={null}
curl --location 'https://api.sandbox.tesouro.com/openid/connect/token' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \
  --data-urlencode 'client_id=CLIENT_ID' \
  --data-urlencode 'client_secret=CLIENT_SECRET' \
  --data-urlencode 'subject_token=USER_JWT_TOKEN' \
  --data-urlencode 'subject_token_type=urn:ietf:params:oauth:token-type:access_token'
```

```json title="Token Exchange Response" lines theme={null}
{
  "access_token": "efJhbGcifiJv4zI1NiIsInR5cCI6IkpXVCJ9...",
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
  "token_type": "Bearer",
  "expires_in": 3600
}
```

<Info>
  **Subject Token Requirements**: Your JWT must include both the user's unique identifier in the `sub` claim and the user's email address in the `email` claim. The returned access token contains RFC 8693 delegation claims indicating it's an impersonated token.
</Info>

<Danger>
  **Security**: Users can only be impersonated within their assigned application context. Cross-application impersonation is prevented for security.
</Danger>

### Widget Authentication

For embedded widgets, your backend generates a **widget token** that securely wraps the user's identity and OAuth credentials using [JWE (RFC 7516)](https://datatracker.ietf.org/doc/html/rfc7516). This token contains an [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token exchange payload that is provided to the widget on the frontend.

**What You Provide:**

* Backend code to generate an encrypted JWE widget token
* The token contains user identity (ID and email) and OAuth credentials
* You control token expiration based on your security requirements

```typescript title="Widget Token Generation" lines expandable theme={null}
async function createWidgetToken(userId: string, userEmail: string) {
  const clientId = process.env.TESOURO_CLIENT_ID;
  const clientSecret = process.env.TESOURO_CLIENT_SECRET;
  const sharedSecret = process.env.TESOURO_WIDGET_SECRET; // 32 bytes

  const now = Math.floor(Date.now() / 1000);
  const secretKey = new TextEncoder().encode(sharedSecret);

  // Step 1: Create subject token (user identity)
  const subjectTokenPayload = {
    sub: userId,
    email: userEmail,
    aud: "tesouro",
    iss: clientId,
    exp: now + 300, // 5 minutes
    iat: now,
  };

  const subjectToken =
    Buffer.from(JSON.stringify({ alg: "none", typ: "JWT" })).toString("base64url") +
    "." +
    Buffer.from(JSON.stringify(subjectTokenPayload)).toString("base64url") +
    ".";

  // Step 2: Create encrypted token
  const payload = {
    aud: "tesouro",
    client_id: clientId,
    client_secret: clientSecret,
    exp: now + 300,
    grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
    iat: now,
    iss: clientId,
    jti: crypto.randomUUID(),
    subject_token: subjectToken,
    subject_token_type: "urn:ietf:params:oauth:token-type:access_token",
  };

  const token = await new EncryptJWT(payload)
    .setProtectedHeader({
      alg: "A256KW",
      enc: "A256GCM",
      kid: clientId,
      typ: "JWT",
      cty: "JWT",
    })
    .encrypt(secretKey);

  return token;
}
```

<Info>
  **Configuration**: Tesouro provides `TESOURO_CLIENT_ID`, `TESOURO_CLIENT_SECRET`, and `TESOURO_WIDGET_SECRET` (exactly 32 bytes) during onboarding.
</Info>

### Additional resources

Learn more about [JWTs](https://jwt.io/introduction?newtab) and [OAuth 2.0 Token Exchange (RFC 8693)](https://datatracker.ietf.org/doc/html/rfc8693)
