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

# GraphQL authentication

> Learn how to authenticate GraphQL requests with the Pylon API

All GraphQL requests to the Pylon API require authentication using OAuth Bearer tokens.

## Authentication header

Include your access token in the `Authorization` header:

```http theme={null}
Authorization: Bearer YOUR_ACCESS_TOKEN
```

## Making authenticated requests

### Using fetch

```javascript theme={null}
const response = await fetch("https://pylon.mortgage/graphql", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${accessToken}`,
  },
  body: JSON.stringify({
    query: `
      query GetDeals {
        deals(first: 10) {
          edges {
            node {
              id
              friendlyId
            }
          }
        }
      }
    `,
  }),
});
```

### Using GraphQL client

Most GraphQL clients support setting default headers:

```javascript theme={null}
import { GraphQLClient } from "graphql-request";

const client = new GraphQLClient("https://pylon.mortgage/graphql", {
  headers: {
    Authorization: `Bearer ${accessToken}`,
  },
});

const data = await client.request(`
  query GetDeals {
    deals(first: 10) {
      edges {
        node {
          id
          friendlyId
        }
      }
    }
  }
`);
```

## Getting access tokens

Access tokens are obtained through the OAuth 2.0 **client credentials** grant. See the [Authentication guide](/guides/getting-started/authentication/overview) for details on:

* Obtaining client credentials
* Exchanging credentials for tokens
* Requesting a new token when the cached one expires

## Token expiration

Access tokens are issued with a fixed lifetime (roughly 24 hours). There is **no refresh-token grant** — when a token expires you simply request a brand-new one from the token endpoint using the same client credentials. Cache the token along with its expiry and re-request when it's expired:

```javascript theme={null}
const TOKEN_ENDPOINT = "https://auth.pylon.mortgage/oauth/token";

let cachedToken = null;
let tokenExpiresAt = 0;

async function getAccessToken() {
  // Reuse the cached token while it's still valid.
  if (cachedToken && Date.now() < tokenExpiresAt) {
    return cachedToken;
  }

  // Expired (or never fetched): request a brand-new token.
  const response = await fetch(TOKEN_ENDPOINT, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      grant_type: "client_credentials",
      client_id: process.env.PYLON_CLIENT_ID,
      client_secret: process.env.PYLON_CLIENT_SECRET,
    }),
  });

  const data = await response.json();
  cachedToken = data.access_token;
  // Refresh slightly early to avoid edge-of-expiry failures.
  tokenExpiresAt = Date.now() + (data.expires_in - 60) * 1000;
  return cachedToken;
}

async function makeGraphQLRequest(query, variables) {
  const token = await getAccessToken();

  const response = await fetch("https://pylon.mortgage/graphql", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${token}`,
    },
    body: JSON.stringify({ query, variables }),
  });

  return response.json();
}
```

## Error responses

Unauthenticated requests return:

```json theme={null}
{
  "errors": [
    {
      "message": "Unauthorized",
      "extensions": {
        "code": "UNAUTHENTICATED"
      }
    }
  ]
}
```

## Best practices

1. **Store credentials securely** - Never expose your client secret or tokens in client-side code
2. **Cache tokens and re-request before expiry** - Reuse a cached token until it expires, then request a new one
3. **Handle errors gracefully** - Surface authentication failures clearly
4. **Use HTTPS** - Always use HTTPS in production
5. **Rotate credentials** - Regularly rotate your client credentials for security
