# Beyond Pricing Partners API

The Partners API enables programmatic integration with Beyond Pricing's revenue management platform for short-term vacation rentals. There are two ways to use it — pick the path that matches you.

## Choose Your Path

| You are… | Start here |
|----------|------------|
| A **partner or company** integrating many Beyond users on a secure backend — white-label products, multi-tenant platforms, server-to-server jobs. Uses OAuth2 client credentials, optional `user_id` / `credential_id` narrowing, and IP allowlists. | [For Partners](getting-started/partners.md) |
| An **individual Beyond user** automating tasks on your own listings — scripts, cron jobs, CLIs, agents. Uses a personal access token (PAT). | [For Personal Users](getting-started/personal-users.md) |

Not sure which applies? If you authenticate with a client secret on a backend you control and manage other people's Beyond accounts, you are a **partner**. If you are automating your own Beyond account, you are a **personal user**. The [Authentication](getting-started/authentication.md) overview compares every mechanism side by side.

## What You Can Do

The Partners API v1 provides programmatic access to:

- **[Listings](endpoints/listings.md)** -- retrieve and manage property listings with pricing data
- **[Calendar](endpoints/calendar.md)** -- access availability and pricing calendars
- **[Users](endpoints/users.md)** -- create and manage user accounts (partner integrations)
- **[Accounts](endpoints/managed-accounts.md)** -- connect channel accounts (PMS, Airbnb, etc.)

## Shared Reference

- **[Authentication](getting-started/authentication.md)** -- OAuth2 client credentials for partners, personal access tokens (`bpat_…`) for individual users, following [RFC 9700](https://datatracker.ietf.org/doc/html/rfc9700)
- **[JSON:API Format](guides/jsonapi.md)** -- consistent request and response structure per the [JSON:API specification](https://jsonapi.org/) (v1.1)
- **[Rate Limiting](guides/rate-limiting.md)** -- transparent limits with response headers for client-side throttling
- **[Error Handling](guides/error-handling.md)** -- standardized error responses with actionable details
- **[Versioning](guides/versioning.md)** -- URL-based versioning with a clear deprecation policy
- **API Reference** -- interactive [Swagger UI](/api/v1/docs/) and [ReDoc](/api/v1/redoc/), auto-generated via OpenAPI
- **[Release Notes](release-notes.md)** -- changelog of API updates and improvements

## AI-Driven Client Implementation

When implementing API clients with AI tools, provide both schema-level and narrative docs context:

- **OpenAPI Specification**: [schema endpoint](/api/v1/schema/) for precise endpoint contracts, field types, and request/response structures
- **Full Documentation (Markdown)**: [single markdown document](/full-documentation.md) for authentication, guides, behavior details, and endpoint usage examples in one file

## Base URL

All API requests use the base URL of your Beyond Pricing Partners deployment (e.g., `https://partners.beyondpricing.com`). Throughout this documentation, curl examples use `$BASE_URL` as a placeholder:

```bash
export BASE_URL=https://partners.beyondpricing.com
```

## Support

For questions about your API integration:

- **Email**: api-support@beyondpricing.com

---

## Getting Started

Choose the path that matches who you are. Beyond Pricing supports two audiences: **partners** integrating many Beyond users on a secure backend, and **personal users** automating their own account.

Start with **[Authentication](authentication.md)** for diagrams and guidance on partner versus personal mechanisms (client credentials and PATs).

### Choose Your Path

#### [For Partners](partners.md)

Use this path if you are a company or large customer integrating many Beyond users on a backend you control — white-label products, multi-tenant platforms, cron jobs, and other server-to-server integrations.

- Client type: confidential
- Default grant: client credentials
- Optional narrowing: user-scoped tokens via `user_id`, optionally further scoped with `credential_id`
- The [For Partners](partners.md) page covers the full end-to-end flow, from token setup to listing activation

#### [For Personal Users](personal-users.md)

Use this path if you are an existing Beyond user automating tasks on your own listings with a CLI, script, or agent.

- App mode: personal
- Authentication: a [personal access token](authentication.md#personal-access-token-pat) (PAT) created from the Beyond dashboard
- Bound to one Beyond login; no client secret to manage
- The [For Personal Users](personal-users.md) page covers the full end-to-end flow, from PAT creation to automation

### Common Prerequisites

Before you begin, you need:

1. Beyond-provisioned OAuth2 credentials or an application created by your operator.
2. The correct base URL for your environment.
3. An HTTP client capable of making REST API calls.

For help provisioning credentials, contact `api-support@beyondpricing.com`.

### Common Endpoints

| Component | URL |
|-----------|-----|
| API Endpoints | `/api/v1/*` |
| Token Endpoint | `/o/token/` |
| Swagger UI | [`/api/v1/docs/`](/api/v1/docs/) |
| ReDoc | [`/api/v1/redoc/`](/api/v1/redoc/) |

---

## Authentication

This page is the overview for all supported ways to call the Partners API (`/api/v1/*`). Step-by-step curl examples live in the audience guides linked below.

New here? Start at [For Partners](partners.md) or [For Personal Users](personal-users.md), then return here when you need the full mechanism reference.

- [For Partners](partners.md) — confidential clients and client credentials
- [For Personal Users](personal-users.md) — personal access tokens

### Choose The Right Mechanism


| Who you are                                                                 | What you should use                                                                             | Why                                                                                                                                                                                                                                                              |
| --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Partner or company** integrating many customers on a secure backend       | **OAuth2 client credentials** with a **confidential** client                                    | Strongest option: client secret never ships to end-user devices, machine-to-machine access, optional per-user and per-credential narrowing with `user_id` and `credential_id`.                                                                                   |
| **Individual Beyond user** automating your own account (CLI, script, agent) | **Personal access token (PAT)** | A PAT is a long-lived, credential-scoped secret you create yourself from the Beyond dashboard. No client secret to manage, no browser step at runtime; ideal for non-interactive automation on your own account. |


Partners should **not** rely on PATs for production integrations: PATs exist only for personal automation applications, not partner multi-tenant apps.

For the personal access token walkthrough, use [For Personal Users](personal-users.md). For partner token requests with `user_id` / `credential_id`, use [For Partners](partners.md).

On every `/api/v1/`* request, send the access token or PAT in the header `Authorization: Bearer <token>`.

### OAuth2: Client Credentials (Confidential Partner)

Typical path: your backend exchanges `client_id` and `client_secret` for an access token, then calls `/api/v1/`*. Optional `user_id` and `credential_id` narrow the token (see [For Partners](partners.md)).

```mermaid
sequenceDiagram
    participant Partner as PartnerBackend
    participant Token as OAuthTokenEndpoint
    participant API as PartnersAPI

    Partner->>Token: POST /o/token/ client_credentials
    Note right of Partner: client_id, client_secret, scope optional user_id credential_id
    Token-->>Partner: access_token, expires_in, token_type Bearer
    Partner->>API: GET /api/v1/... with Authorization Bearer
    API-->>Partner: JSON API response
```



### Shared OAuth2 Rules

The Partners API follows [RFC 9700 - OAuth 2.0 Security Best Current Practice](https://datatracker.ietf.org/doc/html/rfc9700).

#### Token Lifetimes

- OAuth2 access tokens (client credentials) expire after 1 hour.
- Personal access tokens do not expire until revoked.

#### Grant Restrictions


| Grant Type           | Status   | Notes                           |
| -------------------- | -------- | ------------------------------- |
| Client Credentials   | Allowed  | For partner server integrations |
| Implicit             | Disabled | Rejected                        |
| Password             | Disabled | Rejected                        |


#### User-Scoped Tokens

Server integrations can ask `/o/token/` for either an application-level token or a user-scoped token:

- **Application-level token**: omit `user_id`. The token acts for the OAuth application and can access all users owned by that application, subject to the scopes granted on the token.
- **User-scoped token**: include `user_id`. The token is bound to that one user. You may also include `credential_id` to apply one Beyond login's visibility and permissions; without it, Beyond uses the user's primary credential.

If Beyond enables `require_user_scoped_tokens` for your application, user-level resource scopes cannot be used on an application-level token. You must include `user_id` for those scopes. App-level scopes and cross-tier scopes still work without `user_id`.

#### Scope Tiers

All requested scopes must first be enabled for your OAuth application. After that, token tier rules apply:


| Tier       | Scopes                                                                                   | Application-level token, no `user_id`                                                                        | User-scoped token, with `user_id`                                                                             |
| ---------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| App-level  | `user:write`                                                                             | Allowed. Operates on the application as a whole.                                                             | Rejected. App-level scopes cannot be combined with `user_id`.                                                 |
| User-level | `listings:read`, `listings:write`, `reservations:read`, `accounts:read`, `insights:read` | Allowed only when `require_user_scoped_tokens` is disabled for the application. Rejected when it is enabled. | Allowed. Access is limited to the bound user, and to the selected credential when `credential_id` is present. |
| Cross-tier | `user:read`                                                                              | Allowed. Returns users owned by the application.                                                             | Allowed. Returns only the bound user.                                                                         |


#### Scope Reference


| Scope               | Tier       | Description              |
| ------------------- | ---------- | ------------------------ |
| `user:write`        | App-level  | Create and modify users  |
| `user:read`         | Cross-tier | Read user information    |
| `listings:read`     | User-level | Read listing data        |
| `listings:write`    | User-level | Modify listings          |
| `reservations:read` | User-level | Read reservation data    |
| `accounts:read`     | User-level | Read account information |
| `insights:read`     | User-level | Read market insights     |


#### IP Allowlist

When configured for an application, IP allowlists are enforced on both `/o/`* and `/api/v1/`*.

### Personal Access Token (PAT)

PATs are **not** an OAuth2 grant. Beyond users can create and manage their own PATs from the Beyond dashboard user settings when they need a long-lived credential for personal automation. Beyond shows the secret exactly once at creation time; after that, each request uses `Authorization: Bearer` with the PAT. Store it like any other high-value credential. A PAT inherits the permissions of the Beyond login (credential) it was issued for: what listings and accounts the token can read and write follows that credential's configuration on the [Settings / Team page](https://v2.beyondpricing.com/dashboard/user/credentials), and permission changes there apply to the token immediately. IP restrictions, when Beyond has configured them for the app, are enforced the same way as for OAuth2 access tokens.

```mermaid
sequenceDiagram
    participant Client as AutomationClient
    participant API as PartnersAPI

    Client->>API: Authorized request with Bearer PAT
    API-->>Client: JSON API response
```



Use your issued cleartext value in the `Authorization` header. Personal access tokens always start with the `bpat_` prefix.

#### PAT Rules (Summary)

- **Personal automation only** — PATs are for personal automation apps (one Beyond user), not for partner-style multi-customer integrations.
- **One login** — A PAT only ever acts as the Beyond login (credential) it was issued for. It cannot switch to another login under the same user.
- **Self-service creation** — PATs are created from the Beyond dashboard, not minted through `/o/token/`.
- **Permissions follow the credential** — A PAT acts with the permissions of its credential. Manage what the token can read and write through that credential on the [Settings / Team page](https://v2.beyondpricing.com/dashboard/user/credentials); changes apply immediately.
- **No OAuth minting** — PATs are not created or refreshed through `/o/token/`. They are created and revoked from the Beyond dashboard.

---

## For Partners

You are a **partner** if you are a company or large customer integrating many
Beyond users on a backend you control — white-label products, multi-tenant
platforms, or server-to-server automation. Partners authenticate with **OAuth2
client credentials** on a confidential client, can narrow access to a single
Beyond user or login with `user_id` / `credential_id`, and are typically
protected by an IP allowlist.

This is the path for trusted backend services that can store a client secret
securely. This page is the end-to-end path: how authentication and token
scoping work, then the onboarding flow from creating a user to activating a
listing.

If instead you are an individual Beyond user automating your own listings, see
[For Personal Users](personal-users.md).

### When To Use This Path

Choose this profile if your integration:

- Runs on your servers, workers, or scheduled jobs.
- Needs machine-to-machine access without an end-user browser step.
- May optionally narrow access to one Beyond user at a time with `user_id`.
- May optionally narrow a user-scoped token further with `credential_id`.

### Recommended Configuration


| Setting               | Value                           |
| --------------------- | ------------------------------- |
| App mode              | `partner`                       |
| Client type           | `confidential`                  |
| Primary grant         | `client_credentials`            |
| Access token lifetime | 1 hour                          |


For a comparison with personal automation flows and personal access tokens,
read the [Authentication](authentication.md) overview first.

### Get An Access Token

Confidential clients use the client credentials grant at `/o/token/` and then
send the access token on `/api/v1/*` requests.

```mermaid
sequenceDiagram
    participant Partner as PartnerBackend
    participant Token as OAuthTokenEndpoint
    participant API as PartnersAPI

    Partner->>Token: POST /o/token/ grant_type=client_credentials
    Note right of Partner: client_id, client_secret, scope optional user_id credential_id
    Token-->>Partner: access_token, expires_in, token_type Bearer
    Partner->>API: GET /api/v1/... Authorization Bearer access_token
    API-->>Partner: JSON API response
```



```bash
curl -X POST $BASE_URL/o/token/ \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "scope=listings:read user:read"
```

**Response**

```json
{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
  "expires_in": 3600,
  "token_type": "Bearer",
  "scope": "listings:read user:read"
}
```

!!! tip "Building a client with AI"
    If you are scaffolding the client with an AI coding tool, give it both the
    schema-level and narrative context so it generates correct requests:

    - **OpenAPI specification** — [`/api/v1/schema/`](/api/v1/schema/) for
      precise endpoint contracts, field types, and request/response structures.
    - **Full documentation (Markdown)** — [`/full-documentation.md`](/full-documentation.md)
      for authentication, behavior details, and usage examples in one file.

### Optional: Narrow A Token To One User

Partner apps can request a user-scoped token by adding `user_id` to the token
request. This is useful when your system manages multiple Beyond users and you
want a token that can only act on one of them.

```bash
curl -X POST $BASE_URL/o/token/ \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "scope=listings:read user:read" \
  -d "user_id=42"
```

The response includes `user_id` when the token is narrowed successfully.

#### Token Tiers And `require_user_scoped_tokens`

Beyond classifies scopes into tiers. These tiers decide whether a scope can be
used on an application-level token, a user-scoped token, or both.


| Tier       | Scopes                                                                                   | How to request                                                               | Behavior                                                                                                                                                            |
| ---------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| App-level  | `user:write`                                                                             | Omit `user_id`.                                                              | Operates on the application as a whole. Rejected if you include `user_id`.                                                                                          |
| User-level | `listings:read`, `listings:write`, `reservations:read`, `accounts:read`, `insights:read` | Include `user_id` when user-scoped tokens are required for your application. | Acts only on the bound user's resources. If `require_user_scoped_tokens` is disabled, these scopes may also be used without `user_id` for application-level access. |
| Cross-tier | `user:read`                                                                              | Omit or include `user_id`.                                                   | Without `user_id`, reads users owned by the application. With `user_id`, reads only the bound user.                                                                 |


When `require_user_scoped_tokens` is enabled for your application, token
requests without `user_id` can still use app-level and cross-tier scopes
(`user:write`, `user:read`), but not user-level scopes. Requests with `user_id`
can use user-level and cross-tier scopes, but never `user:write`.

### Optional: Narrow A User-Scoped Token To One Credential

If you want the token to behave exactly like one login credential within the
user, add `credential_id`. When omitted, Beyond uses the user's primary
credential by default.

```bash
curl -X POST $BASE_URL/o/token/ \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "scope=listings:read user:read" \
  -d "user_id=42" \
  -d "credential_id=314"
```

When `credential_id` is present, listing visibility and write access are
constrained by that credential's global permissions and grants.

#### Credential-scoped visibility rules

Once a token is bound to a credential, every endpoint that supports user-scoped
tokens applies the rules below. The rules are identical for listings and managed
accounts: managed accounts are narrowed only when at least one of their listings
would also be narrowed.


| `credential_id` present | Credential `global_permissions` | What the token can see and do                                                                                                                                                                        |
| ----------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| No (app-level token)    | n/a                             | All resources owned by every user of the application.                                                                                                                                                |
| Yes                     | `ADMIN`                         | All listings and managed accounts owned by the bound user. `GET /users/<id>/credentials/` returns every non-deleted credential. Can read resources and use listing write endpoints.                  |
| Yes                     | `EDIT`                          | All listings and managed accounts owned by the bound user. `GET /users/<id>/credentials/` returns only the acting credential. Can read resources and use listing write endpoints.                    |
| Yes                     | `VIEW`                          | Same visibility as `EDIT`, but listing write endpoints return `403 Forbidden`.                                                                                                                       |
| Yes                     | `NONE`                          | Only listings explicitly granted to the credential; managed accounts are filtered to those that own at least one granted listing. `GET /users/<id>/credentials/` returns only the acting credential. |


Soft-deleting a credential (for example, when a user removes a login) revokes
the credential for API purposes: any token bound to it will start returning
`403 Forbidden` immediately, and new token requests that resolve to a
soft-deleted credential are rejected at `/o/token/`.

### Make Your First API Call

```bash
curl -X GET "$BASE_URL/api/v1/listings/?page[size]=5" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/vnd.api+json"
```

### End-to-End Onboarding Flow

Once your OAuth2 application has been provisioned, follow this flow to take a
partner's user from creation to a listing that Beyond Pricing is actively
pricing. It focuses on the order of operations and the background sync
lifecycle, and links to the endpoint pages that document request details.

1. Get an application-level token
2. Create a user
3. Add an account for that user
4. Get a user-scoped token for that user
5. Wait for the background sync to finish
6. List the user's listings and map Beyond Pricing listing IDs to your listing IDs
7. Enable the listing so the integration framework will begin pushing prices to the account

```mermaid
sequenceDiagram
    participant Partner as PartnerSystem
    participant BP as BeyondPricing

    Partner->>BP: Get app-level token
    Partner->>BP: Create user
    BP-->>Partner: Return user_id
    Partner->>BP: Add account for user
    BP-->>Partner: Queue sync
    Partner->>BP: Get user-scoped token for user
    loop Until sync completes
        Partner->>BP: List accounts for user
        BP-->>Partner: sync-status = queued/in_progress/completed
    end
    Partner->>BP: List user's listings with user-scoped token
    BP-->>Partner: listing id + channel-listings[].channel-id
    Partner->>BP: Update listing activation
    BP-->>Partner: Price syncing enabled
```

#### Step 1: Get an App-Level Token

Start with an application-level token so you can create the user and add the
user's account. See [Get An Access Token](#get-an-access-token) above for the
client credentials request.

!!! note
    In this onboarding flow, user and account creation use an app-level token
    with `user:write`. After the account is created, request a user-scoped
    token for that user so you can poll account status with `user:read` and
    retrieve listings with `listings:read`.

#### Step 2: Create a User

Create the partner's user first. The user is the container for the account and
for the listings that will later appear in Beyond Pricing.

Use the [Create User](../endpoints/users.md#create-user) endpoint details for
the request and response schema.

#### Step 3: Add an Account for the User

After the user exists, add the partner's channel or PMS account for that user.
This is the step that starts the import process for listings and reservation
history.

Use the [Add Account](../endpoints/managed-accounts.md#add-account) endpoint
details for channel credentials, supported integrations, and response behavior.

#### Step 4: Get a User-Scoped Token

After the account exists, request a
[user-scoped token](#optional-narrow-a-token-to-one-user) for that user. Use
this token for user-specific reads in the remaining steps of the onboarding
flow.

For collection endpoints such as
[List Listings](../endpoints/listings.md#list-listings), a user-scoped token
automatically limits the response to the bound user, so you do not usually need
to add `filter[owner]={user_id}`.

#### Step 5: Wait for the Background Sync

After the account is created, Beyond Pricing starts a background sync that
fetches reservation history for the listings associated with that account. This
can take some time, especially for accounts with many listings or a large
history.

Check the account's `sync-status` by polling `/api/v1/users/{user_id}/accounts`
with the [List Accounts](../endpoints/managed-accounts.md#list-accounts)
endpoint until the state reaches `completed`.

```json
{
  "sync-status": {
    "state": "completed",
    "last-successful-sync-at": "2026-03-06T11:00:00+00:00"
  }
}
```

!!! warning
    Do not try to map listings or enable price syncing before the account sync
    has finished. Until sync is complete, the listing set may still be
    incomplete.

#### Step 6: Map Beyond Pricing Listing IDs to Your Listing IDs

Once the sync has completed, retrieve listings with the
[List Listings](../endpoints/listings.md#list-listings) endpoint using the
user-scoped token for that user. The response is already limited to that user's
listings.

The Beyond Pricing listing ID is the top-level resource `id`. Your listing ID
from the connected channel or PMS is available in
`channel-listings[].channel-id`.

```json
{
  "id": "12345",
  "channel-listings": [
    {
      "channel": "hostaway",
      "channel-id": "external-listing-987"
    }
  ]
}
```

See the [Listings](../endpoints/listings.md) endpoint reference for filtering
behavior, and the [JSON:API guide](../guides/jsonapi.md#filtering) for
collection query conventions.

#### Step 7: Enable a Listing

Listings are created disabled on the Beyond Pricing side. After you have mapped
the listing IDs, enable the listing so Beyond Pricing can start pushing prices
to the connected account.

Use the
[Listing Activation](../endpoints/listings.md#listing-activation) endpoint
reference for the activation payload and response shape.

!!! note
    After a listing is activated, change its nightly base price through the
    [Base Price customization](../endpoints/listing-customizations.md#base-price)
    endpoint. The base price is the anchor used by Beyond Pricing's algorithm,
    so keeping it accurate ensures recommended prices stay aligned with the
    partner's expectations.

#### After Activation

After a listing has been enabled, Beyond Pricing begins the normal
price-syncing lifecycle for that listing.

If you need to inspect the listing again after activation, use the
[Get Listing Details](../endpoints/listings.md#get-listing-details) endpoint.
For API-wide error semantics, see [Error Handling](../guides/error-handling.md).

### Security Notes

- Request only the scopes your integration needs.
- If Beyond has configured an IP allowlist for your application, the same IP
  checks apply to both `/o/*` and `/api/v1/*`.
- `user:write` is app-level only and cannot be combined with `user_id`.
- When `require_user_scoped_tokens` is enabled, request a user-scoped token
  with `user_id` for `listings:read`, `listings:write`, `reservations:read`,
  `accounts:read`, and `insights:read`.
- `user:read` is cross-tier: application-level tokens read all users owned by
  the app; user-scoped tokens read only the bound user.
- User-scoped tokens inherit the selected credential's permissions. A
  credential with limited listing grants will see and modify only the listings
  it is allowed to access.

### Common Pitfalls

- **Using the wrong token type.** User and account creation require an
  app-level token, while listing retrieval is expected to use a user-scoped
  token for the target user. See
  [Narrow A Token To One User](#optional-narrow-a-token-to-one-user).
- **Starting listing mapping too early.** Wait until account `sync-status` is
  `completed`.
- **Confusing Beyond Pricing listing IDs with external channel or PMS listing
  IDs.** Use the listing resource `id` for Beyond Pricing and
  `channel-listings[].channel-id` for your external identifier.
- **Forgetting that field names are dasherized in API payloads.** See
  [JSON:API Format](../guides/jsonapi.md#key-conventions).

### Next Steps

- Review [rate limiting](../guides/rate-limiting.md) before scheduling frequent
  jobs.
- Browse the [endpoint reference](../endpoints/index.md).

---

## For Personal Users

You are a **personal user** if you already have a Beyond account with your own
listings and you want to automate tasks on it — scripts, cron jobs, CLIs, or
agents acting on your own listings. You do not run a multi-tenant backend and
you are not managing other people's Beyond accounts. If you are a company
integrating many Beyond users on a secure backend, use the
[For Partners](partners.md) path instead.

Your Beyond account and listings already exist, so — unlike partners — you do
not create users or add channel accounts through the API. This page is the
end-to-end path: how authentication works, how to set it up from the dashboard,
your first authenticated call, and what you can automate afterward.

### When To Use This Path

Choose this profile if your client:

- Represents one Beyond user automating their own account.
- Runs non-interactively (scripts, cron jobs, agents) and can store a single
  secret securely.
- Does not need to act on behalf of other Beyond users.


### Authentication Method

Personal users authenticate with a **personal access token (PAT)**: a
long-lived secret you create yourself from the
[Beyond Settings](https://v2.beyondpricing.com/dashboard/user/personal-access-tokens),
bound to your single Beyond credential. There is no client secret to manage and
no browser step at runtime — you create the PAT once in the dashboard, then
send it on every API request.

The PAT inherits the permissions of the credential it is bound to. What the
token can read and write is controlled by that credential's configuration on
the [Settings / Team page](https://v2.beyondpricing.com/dashboard/user/credentials)
— the same view and per-listing access you have in the dashboard. Changes to
those permissions take effect immediately for the token.

See [Authentication](authentication.md#personal-access-token-pat) for the full
PAT rules and credential scoping reference.

### End-to-End Flow

1. Sign in to the Beyond dashboard
2. Create a personal access token (PAT) from your dashboard user settings
3. Make your first authenticated API call and confirm it returns your listings
4. Read and write data for your listings (pricing, calendar, customizations,
   recommendations)

```mermaid
sequenceDiagram
    participant User as PersonalUser
    participant Dashboard as BeyondDashboard
    participant BP as BeyondPricing

    User->>Dashboard: Sign in
    User->>Dashboard: Create PAT
    Dashboard-->>User: Show bpat_ secret once
    User->>BP: GET /api/v1/listings/ with Authorization Bearer bpat_…
    BP-->>User: Your listings
    User->>BP: Read/write pricing, calendar, customizations
    BP-->>User: Updated data
```

### Step 1: Sign In to the Beyond Dashboard

Sign in at [https://v2.beyondpricing.com](https://v2.beyondpricing.com) with
your Beyond credentials.

### Step 2: Create a Personal Access Token

Create a PAT from your dashboard user settings (Personal Access Tokens). Beyond shows the
cleartext secret **exactly once** at creation time — copy and store it
immediately, like any other high-value credential.

The PAT does not have its own permission settings — it acts with the
permissions of the credential it is bound to. To control which listings the
token can read or modify, manage that credential on the
[Settings / Team page](https://v2.beyondpricing.com/dashboard/user/credentials).
Permission changes there apply to the token immediately.

### Step 3: Make Your First API Call

Send the PAT in the `Authorization` header. A PAT is already bound to your
user, so the response is limited to your own listings — you do not need a
`filter[owner]` parameter.

```bash
curl -X GET "$BASE_URL/api/v1/listings/?page[size]=5" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/vnd.api+json"
```

If the response lists your properties, authentication is working.

### Step 4: Automate Your Tasks

Call the endpoints your automation needs:

- [Listings](../endpoints/listings.md) — read listing details
- [Calendar](../endpoints/calendar.md) — read availability and pricing calendars
- [Listing Customizations](../endpoints/listing-customizations.md) — adjust base
  price, min price, and other settings
- [Recommendations](../endpoints/recommendations.md) — read pricing
  recommendations

For API-wide error semantics, see [Error Handling](../guides/error-handling.md),
and review [Rate Limiting](../guides/rate-limiting.md) before scheduling
frequent jobs.

### Rotate or Revoke

Revoke a PAT from the same dashboard settings at any time; a revoked token
immediately stops working. The secret cannot be recovered after creation — if
you lose it, revoke it and create a new one.

### Example Use Cases

These are common automations a personal user can build with
basic-to-intermediate development skills. Each one only needs your PAT and a
scheduled script (for example, a daily cron job).

!!! tip "Building a client with AI"
    If you are scaffolding the client with an AI coding tool, give it both the
    schema-level and narrative context so it generates correct requests:

    - **OpenAPI specification** — [`/api/v1/schema/`](/api/v1/schema/) for
      precise endpoint contracts, field types, and request/response structures.
    - **Full documentation (Markdown)** — [`/full-documentation.md`](/full-documentation.md)
      for authentication, behavior details, and usage examples in one file.

    This is the fastest way to turn the use cases below into working code.

#### 1. Pricing recommendation digest

Get notified about Beyond's latest base-price suggestions and, optionally,
apply the ones you trust automatically — instead of logging into the dashboard
to check.

How to build it: list your listings with
[Listings](../endpoints/listings.md), then for each listing read pending
suggestions from [Recommendations](../endpoints/recommendations.md). Summarize
them into an email or chat message. To act on a suggestion, push the value with
the `base-price` [Listing Customization](../endpoints/listing-customizations.md).
Run it on a daily schedule.

#### 2. Occupancy and revenue report

Export a forward-looking view of bookings and nightly prices across all your
listings into a spreadsheet or BI dashboard, so you can track occupancy and
projected revenue without manual data entry.

How to build it: enumerate your listings with
[Listings](../endpoints/listings.md), then for each one pull the daily
[Calendar](../endpoints/calendar.md) for your reporting window (using
`filter[start-date]` and `filter[end-date]`). The calendar returns availability
status and price per night — aggregate those into occupancy and revenue figures
and write them to a CSV or sheet on a daily or weekly cadence.

#### 3. Seasonal rules automation

Apply consistent pricing and stay rules across your whole portfolio ahead of a
high or low season — for example, raising price floors and requiring longer
minimum stays for summer — without editing each listing by hand.

How to build it: list your listings with
[Listings](../endpoints/listings.md), then apply the rules per listing through
[Listing Customizations](../endpoints/listing-customizations.md): use
`min-max-prices` for seasonal price floors and ceilings and `min-stays` for
seasonal minimum-night rules. Run the script once when a season is approaching,
or keep the desired rules in a config file and reconcile on a schedule.

### Security Notes

- The PAT secret is shown only once at creation. Store it like any other
  high-value credential.
- The token acts with its credential's permissions. Keep that credential's
  access on the [Settings / Team page](https://v2.beyondpricing.com/dashboard/user/credentials)
  scoped to what your automation needs.
- Revoke immediately if a token is exposed, then create a replacement.

### Common Pitfalls

- **Losing the PAT secret.** Beyond shows it only once at creation. If you lose
  it, revoke it and create a new one — it cannot be retrieved later.
- **Expecting more access than the credential allows.** The token cannot do
  more than its credential permits. If a call is denied, check that credential's
  permissions on the
  [Settings / Team page](https://v2.beyondpricing.com/dashboard/user/credentials).
- **Forgetting that field names are dasherized in API payloads.** See
  [JSON:API Format](../guides/jsonapi.md#key-conventions).

### Next Steps

- Review [error handling](../guides/error-handling.md) and
  [rate limiting](../guides/rate-limiting.md).
- Browse the [endpoint reference](../endpoints/index.md).

---

## Guides

These guides cover the key concepts and patterns you need to build a robust integration with the Beyond Pricing Partners API.

| Guide | Description |
|-------|-------------|
| [For Partners](../getting-started/partners.md) | End-to-end flow for partners: token setup and scoping, creating a user, adding an account, waiting for sync, mapping listing IDs, and enabling price syncing |
| [For Personal Users](../getting-started/personal-users.md) | End-to-end flow for an individual Beyond user automating their own listings: dashboard auth setup, first call, and example automations |
| [JSON:API Format](jsonapi.md) | Request/response structure, field naming, content types, pagination, sorting, and filtering |
| [Rate Limiting](rate-limiting.md) | Understand rate limits and implement retry logic |
| [Error Handling](error-handling.md) | Error response format and common error codes |
| [Versioning](versioning.md) | API versioning policy and backward compatibility |

---

## JSON:API Format

The Partners API follows the [JSON:API specification](https://jsonapi.org/) (v1.1) for all request and response formatting.

### Key Conventions

- **Resource objects** include `type`, `id`, and `attributes`
- **Field names** are dasherized (e.g., `base-price` instead of `base_price`)
- **Resource types** are pluralized (e.g., `listings`, `users`)

### Content-Type

Requests should use the JSON:API media type:

```
Content-Type: application/vnd.api+json
```

The API also accepts `application/json` for backward compatibility.

### Response Format

#### Single Resource

```json
{
  "data": {
    "type": "listings",
    "id": "12345",
    "attributes": { ... }
  }
}
```

#### Collection

Collections include `meta` with pagination info and `links` for navigation (see [Pagination](#pagination)):

```json
{
  "data": [ ... ],
  "meta": { "pagination": { "count": 150, "page": 1, "pages": 6 } },
  "links": { "first": "...", "last": "...", "next": "...", "prev": null }
}
```

#### Error Response

```json
{
  "errors": [
    {
      "status": "404",
      "title": "Not Found",
      "detail": "Listing 12345 not found",
      "source": {"pointer": null}
    }
  ]
}
```

See [Error Handling](error-handling.md) for the full list of error codes and best practices.

### Request Format (POST/PATCH)

When creating or updating resources, wrap the payload in a `data` object:

```json
{
  "data": {
    "type": "users",
    "attributes": {
      "first-name": "John",
      "last-name": "Doe",
      "email": "john@example.com"
    }
  }
}
```

### Compound Documents (Sideloading)

Some endpoints support including related resources in a single request using the `?include=` parameter. This eliminates the need for multiple API calls.

```
GET /api/v1/listings/12345/?include=owner
```

Related resources appear in the top-level `included` array, with the relationship declared in `relationships`:

```json
{
  "data": {
    "type": "listings",
    "id": "12345",
    "attributes": { ... },
    "relationships": {
      "owner": { "data": {"type": "users", "id": "789"} }
    }
  },
  "included": [
    {
      "type": "users",
      "id": "789",
      "attributes": { ... }
    }
  ]
}
```

See each endpoint's documentation in the [Swagger UI](/api/v1/docs/) for the list of supported `include` values.

### Pagination

Collection endpoints return paginated results. Use query parameters to control pagination, sorting, and filtering.

All collection endpoints use page-based pagination:

| Parameter | Description | Default |
|-----------|-------------|---------|
| `page[number]` | Page number (1-indexed) | 1 |
| `page[size]` | Items per page (max 100) | 25 |

Responses include pagination info in `meta` and navigation links in `links`:

```json
{
  "meta": {
    "pagination": {
      "count": 150,
      "page": 1,
      "pages": 6
    }
  },
  "links": {
    "first": "...?page%5Bnumber%5D=1",
    "last": "...?page%5Bnumber%5D=6",
    "next": "...?page%5Bnumber%5D=2",
    "prev": null
  }
}
```

Use the `links` URLs to navigate between pages without constructing URLs manually.

### Sorting

Use the `sort` parameter with field names. Prefix with `-` for descending order. Multiple sort fields are comma-separated.

```bash
# Newest first
?sort=-created_at

# Multiple fields: by city ascending, then by price descending
?sort=city,-base_price
```

Each endpoint documents its own sortable fields in the [Swagger UI](/api/v1/docs/).

### Filtering

Use `filter[field]` parameters to narrow results.

```bash
# Single filter
?filter[enabled]=true

# Multiple filters
?filter[owner]=123&filter[enabled]=true
```

Each endpoint documents its own available filters in the [Swagger UI](/api/v1/docs/).

### Putting It All Together

Combine pagination, sorting, and filtering in a single request:

```bash
curl -X GET "$BASE_URL/api/v1/listings/?page[number]=1&page[size]=25&sort=-created_at&filter[enabled]=true" \
  -H "Authorization: Bearer <token>" \
  -H "Accept: application/vnd.api+json"
```

---

## Rate Limiting

The API enforces rate limits to protect platform stability. Limits are applied per OAuth2 application.

### How It Works

#### Application-Level Rate Limit

Every OAuth2 application has a configurable rate limit. This is a global bucket -- all requests to any endpoint count toward the same limit.

#### View-Level Rate Limit

Some endpoints enforce a stricter per-endpoint limit in addition to the application-level limit. When configured, each (application, endpoint) pair gets its own counter. Both limits must pass for a request to succeed.

### Response Headers

Every response includes rate-limit headers reflecting the most constrained active limit:

| Header | Description |
|--------|-------------|
| `X-RateLimit-Limit` | Maximum requests allowed in the current window |
| `X-RateLimit-Remaining` | Requests remaining in the current window |
| `X-RateLimit-Reset` | Seconds until the window resets |

### Rate Limit Exceeded (429)

When the rate limit is exceeded, the API responds with HTTP 429 and a `Retry-After` header:

```json
{
  "errors": [
    {
      "status": "429",
      "detail": "Request was throttled. Expected available in 42 seconds.",
      "source": {"pointer": "/data"}
    }
  ]
}
```

**Headers on 429 responses:**

```
Retry-After: 42
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 42
```

### Best Practices

- **Monitor headers**: check `X-RateLimit-Remaining` to throttle requests client-side before hitting the limit
- **Respect `Retry-After`**: always wait the indicated time before retrying after a 429
- **Exponential backoff**: use exponential backoff when retrying after rate-limit errors
- **Request only what you need**: use pagination with reasonable page sizes and filter results to reduce the number of API calls

---

## Error Handling

All error responses follow the JSON:API error format, providing structured information about what went wrong.

### Error Response Structure

```json
{
  "errors": [
    {
      "status": "400",
      "title": "Bad Request",
      "detail": "This field is required.",
      "source": {"pointer": "/data/attributes/email"}
    }
  ]
}
```


| Field            | Description                                                               |
| ---------------- | ------------------------------------------------------------------------- |
| `status`         | HTTP status code as a string                                              |
| `title`          | Short, human-readable summary of the error                                |
| `detail`         | Specific explanation of what went wrong                                   |
| `source.pointer` | JSON pointer to the request field that caused the error (when applicable) |


### Common Error Codes

#### 400 -- Bad Request

Malformed requests or request-body validation failures detected while parsing the
JSON:API document and validating request attributes. This includes both
structural issues and semantic or value validation errors raised while handling
the request body.

```json
{
  "errors": [
    {
      "status": "400",
      "title": "Bad Request",
      "detail": "This field is required.",
      "source": {"pointer": "/data/attributes/email"}
    }
  ]
}
```

When more than one request field is invalid, the response may include multiple
error objects. For example:

```json
{
  "errors": [
    {
      "status": "400",
      "title": "Bad Request",
      "detail": "Ensure this value is greater than or equal to 1.",
      "source": {"pointer": "/data/attributes/extra-guest-fee"},
      "code": "min_value"
    },
    {
      "status": "400",
      "title": "Bad Request",
      "detail": "Ensure this value is greater than or equal to 1.",
      "source": {"pointer": "/data/attributes/extra-guest-threshold"},
      "code": "min_value"
    }
  ]
}
```

**Token request errors:** When requesting a token at `/o/token/`, you may
receive an error if `user_id` references a user that doesn't exist or isn't
owned by your application, `credential_id` does not belong to the provided
`user_id`, `user_id` is provided with app-level scopes such as `user:write`, or
user-level scopes are requested without `user_id` when your application
requires user-scoped tokens. These return a standard OAuth2 error response, not
JSON:API format, with `"error": "invalid_scope"`.

#### Request ID Header

You may send an optional `X-Request-ID` header on any Public API request to make
log correlation easier when debugging with Beyond support.

- If you send a valid UUID, the API uses that value after normalizing it to the
canonical lowercase hyphenated form.
- If you omit the header, or send it blank or whitespace-only, the API
generates a UUID for the request.
- Every response except the `/healthz` fast-path includes the effective
`X-Request-ID` header so you can log or report the exact value the server
used.
- If you send a non-empty `X-Request-ID` that is not a valid UUID, the request
is rejected with `400 Bad Request`.

For JSON:API endpoints, the error response identifies the header explicitly:

```json
{
  "errors": [
    {
      "status": "400",
      "title": "Bad Request",
      "detail": "X-Request-ID header must be a valid UUID.",
      "source": {"header": "X-Request-ID"}
    }
  ]
}
```

For non-JSON:API endpoints such as `/o/token/`, the API returns a simpler JSON
error body:

```json
{
  "error": "X-Request-ID header must be a valid UUID."
}
```

#### 401 -- Unauthorized

Missing or invalid authentication token.

```json
{
  "errors": [
    {
      "status": "401",
      "title": "Unauthorized",
      "detail": "Authentication credentials were not provided."
    }
  ]
}
```

!!! tip
    If you receive a 401, check that your access token has not expired. Tokens are valid for 1 hour. See [Token Lifetimes](../getting-started/authentication.md#token-lifetimes).

#### 403 -- Forbidden

The token is valid but lacks the required scope for this operation, or the token is scoped to a different user.

```json
{
  "errors": [
    {
      "status": "403",
      "title": "Forbidden",
      "detail": "You do not have permission to perform this action."
    }
  ]
}
```

Common causes:

- **Missing scope**: The token doesn't include the required scope for this endpoint.
- **User-scoped token restriction**: A user-scoped token attempted an app-level operation (e.g., creating or deleting a user).
- **Cross-user access**: A user-scoped token attempted to access resources belonging to a different user.
- **Source IP not allowed**: Your application has an IP allowlist configured and the request origin IP is not in that allowlist (applies to both `/o/`* OAuth2 endpoints and `/api/v1/*` resource endpoints).

#### 404 -- Not Found

The requested resource does not exist or is not accessible to your application.

```json
{
  "errors": [
    {
      "status": "404",
      "title": "Not Found",
      "detail": "Listing 12345 not found"
    }
  ]
}
```

#### 422 -- Unprocessable Entity

The JSON:API document is structurally valid and request-body validation has
already passed, but the operation is still rejected during downstream domain or
integration processing. This includes channel-specific validation or
authentication failures that are not simple request-body field errors.

```json
{
  "errors": [
    {
      "status": "422",
      "source": {"pointer": "/data/attributes/credentials"},
      "title": "Invalid Credentials",
      "detail": "Channel 'hostaway': Invalid client credentials",
      "meta": {"channel": "hostaway", "code": "invalid_credentials"}
    }
  ]
}
```

#### 429 -- Too Many Requests

Rate limit exceeded. See [Rate Limiting](rate-limiting.md) for details on handling this error.

#### 500 -- Internal Server Error

An unexpected server error. If this persists, contact
`[api-support@beyondpricing.com](mailto:api-support@beyondpricing.com)`.

### Best Practices

- **Check the `status` field** to determine the error category
- **Use `source.pointer`** to identify which request field caused validation errors
- **Expect multiple error objects** when more than one request attribute is invalid
- **Log the full error response** for debugging
- **Implement retry logic** with exponential backoff for 429 and 5xx errors
- **Do not retry** 400, 401, 403, 404, or 422 errors without changing the request

---

## API Versioning

The Partners API uses URL-based versioning to provide a stable integration surface while allowing the API to evolve.

### Versioning Scheme

The API version is embedded in the URL path:

```
$BASE_URL/api/v1/listings/
              ^^
           version
```

The current version is **v1**. When a new version is released, it will be available at `/api/v2/` while the previous version continues to operate.

### When a New Version Is Created

A new API version is created when a change would **break existing integrations**. Breaking changes include:

- **Removing an endpoint** or HTTP method
- **Removing a response field** or changing its type
- **Removing or renaming a query parameter**
- **Changing the meaning of an existing field** (e.g., a field that was a string becoming an integer)
- **Changing authentication or authorization requirements** in a way that invalidates existing tokens or scopes
- **Changing error response codes** for existing error conditions (e.g., a 400 becoming a 422)
- **Changing the default behavior** of an endpoint (e.g., default sort order, default page size)

### Non-Breaking Changes (No New Version)

The following changes are considered **backward-compatible** and will be made to the current version without creating a new one:

- **Adding a new endpoint**
- **Adding a new optional query parameter**
- **Adding a new field to a response** (your client should ignore unknown fields)
- **Adding a new optional field to a request body**
- **Adding a new scope**
- **Adding a new error code** for a previously-unhandled condition
- **Improving error messages** (the `detail` text may change)
- **Performance improvements** or bug fixes

!!! warning
    Your integration should be built to tolerate additive changes. Always ignore unknown fields in responses rather than failing on them.

### Deprecation Policy

When a new version is released:

1. **Both versions run in parallel** for a migration period
2. **Deprecation notice** is communicated via email and documented here
3. **Migration guide** is provided with a mapping of changes between versions
4. **Sunset date** is announced at least 6 months in advance
5. After the sunset date, the deprecated version returns `410 Gone`

### Version Lifecycle

| Phase | Description |
|-------|-------------|
| **Active** | Fully supported, receives bug fixes and non-breaking enhancements |
| **Deprecated** | Still operational, but a newer version is available. No new features. |
| **Sunset** | Decommissioned. Requests return `410 Gone`. |

The current version, **v1**, is **Active**.

---

## API Endpoints

This section documents all available Partners API v1 endpoints. For full request/response schemas with the ability to make live calls, use the interactive API reference tools:

| Tool | URL | Description |
| ---- | --- | ----------- |
| **Swagger UI** | [`/api/v1/docs/`](/api/v1/docs/) | Interactive API explorer -- browse endpoints, try requests, view schemas |
| **ReDoc** | [`/api/v1/redoc/`](/api/v1/redoc/) | Clean, readable API reference |
| **OpenAPI Schema** | [`/api/v1/schema/`](/api/v1/schema/) | Downloadable OpenAPI 3.0 spec (YAML) for code generation |

To download the schema for use with code generators:

```bash
curl -o openapi.yaml $BASE_URL/api/v1/schema/
```

### Endpoint Summary

| Endpoint | Method | Scope | Token Tier | Description |
| -------- | ------ | ----- | ---------- | ----------- |
| [`/api/v1/listings/`](listings.md) | GET | `listings:read` | Both | List all listings |
| [`/api/v1/listings/<id>/`](listings.md#get-listing-details) | GET | `listings:read` | Both | Get listing details |
| [`/api/v1/listings/<id>/activation/`](listings.md#listing-activation) | PATCH | `listings:write` | Both | Enable or disable price syncing for a listing |
| [`/api/v1/listings/<id>/refresh/`](listings.md#listing-reservations-refresh) | POST | `listings:write` | Both | Queue a reservations refresh for a listing |
| [`/api/v1/listings/<id>/calendar/`](calendar.md) | GET | `listings:read` | Both | Get listing calendar |
| [`/api/v1/listings/<id>/compset/`](compsets.md) | GET | `compsets:read` | Both | Get the precomputed competitive set for a listing |
| [`/api/v1/listings/<id>/recommendations/`](recommendations.md) | GET | `listings:read` | Both | List recommendations for a listing |
| [`/api/v1/listings/<id>/customizations/min-stays/`](listing-customizations.md) | GET | `listings:read` | Both | Get min-stays customization for a listing |
| [`/api/v1/listings/<id>/customizations/min-stays/`](listing-customizations.md) | PATCH | `listings:write` | Both | Update min-stays customization for a listing |
| [`/api/v1/listings/<id>/customizations/extra-guest-fees/`](listing-customizations.md#extra-guest-fees) | GET | `listings:read` | Both | Get extra guest fee customization for a listing |
| [`/api/v1/listings/<id>/customizations/extra-guest-fees/`](listing-customizations.md#extra-guest-fees) | PATCH | `listings:write` | Both | Update extra guest fee customization for a listing |
| [`/api/v1/listings/<id>/customizations/max-stays/`](listing-customizations.md) | GET | `listings:read` | Both | Get max-stays customization for a listing |
| [`/api/v1/listings/<id>/customizations/max-stays/`](listing-customizations.md) | PATCH | `listings:write` | Both | Update max-stays customization for a listing |
| [`/api/v1/listings/<id>/customizations/min-max-prices/`](listing-customizations.md) | GET | `listings:read` | Both | Get min/max prices customization for a listing |
| [`/api/v1/listings/<id>/customizations/min-max-prices/`](listing-customizations.md) | PATCH | `listings:write` | Both | Update min/max prices customization for a listing |
| [`/api/v1/listings/<id>/customizations/time-based-adjustments/`](listing-customizations.md) | GET | `listings:read` | Both | Get time-based adjustments customization for a listing |
| [`/api/v1/listings/<id>/customizations/time-based-adjustments/`](listing-customizations.md) | PATCH | `listings:write` | Both | Update time-based adjustments customization for a listing |
| [`/api/v1/listings/<id>/customizations/checkin-checkout-days/`](listing-customizations.md) | GET | `listings:read` | Both | Get checkin/checkout days customization for a listing |
| [`/api/v1/listings/<id>/customizations/checkin-checkout-days/`](listing-customizations.md) | PATCH | `listings:write` | Both | Update checkin/checkout days customization for a listing |
| [`/api/v1/users/`](users.md) | GET | `user:read` | Both | List users |
| [`/api/v1/users/`](users.md#create-user) | POST | `user:write` | App-level only | Create a user |
| [`/api/v1/users/<id>/credentials/`](users.md#list-user-credentials) | GET | `user:read` | Both | List credentials for a user |
| [`/api/v1/users/<id>/`](users.md#delete-user) | DELETE | `user:write` | App-level only | Delete a user |
| [`/api/v1/users/<id>/accounts/`](managed-accounts.md) | GET | `user:read` | Both | List accounts for a user |
| [`/api/v1/users/<id>/accounts/`](managed-accounts.md#add-account) | POST | `user:write` | Both | Add an account for a user |
| [`/api/v1/users/<id>/accounts/<id>/refresh/`](managed-accounts.md#refresh-account) | POST | `user:write` | Both | Queue a full refresh for an account |
| [`/api/v1/users/<id>/accounts/<id>/`](managed-accounts.md#delete-account) | DELETE | `user:write` | Both | Delete an account |

**Token Tier key:**

- **App-level only**: Requires an application-level token (no `user_id`).
- **Both**: Works with either an app-level token (all resources) or a user-scoped token (bound user's resources only).

See [User-Scoped Tokens](../getting-started/authentication.md#user-scoped-tokens) for details.

### Common Patterns

All endpoints follow the same conventions:

- **Authentication**: OAuth2 Bearer token in the `Authorization` header
- **Content-Type**: `application/vnd.api+json`
- **Response format**: [JSON:API](../guides/jsonapi.md)
- **Pagination**: [Page-based](../guides/jsonapi.md#pagination) with `page[number]` and `page[size]`
- **Errors**: Structured [JSON:API error responses](../guides/error-handling.md)

---

## Listings

| Endpoint | Method | Scope | Description |
| -------- | ------ | ----- | ----------- |
| `/api/v1/listings/` | GET | `listings:read` | List all listings (paginated) |
| `/api/v1/listings/<id>/` | GET | `listings:read` | Get listing details |
| `/api/v1/listings/<id>/activation/` | PATCH | `listings:write` | Enable or disable price syncing for a listing |
| `/api/v1/listings/<id>/refresh/` | POST | `listings:write` | Queue a reservations refresh for a listing |
| `/api/v1/listings/<id>/customizations/<type>/` | GET, PATCH | `listings:read`, `listings:write` | Retrieve or update listing customizations |

!!! tip "Try it out"
    Explore parameters, schemas, and live requests in the [Swagger UI](/api/v1/docs/#/Listings).

### List Listings

Returns a paginated list of all listings for the authenticated application. Supports [pagination and sorting](../guides/jsonapi.md#pagination), and filtering by owner and enabled status.

### Get Listing Details

Returns detailed information about a specific listing.

#### Compound Documents (Sideloading)

Include related resources in a single request using the `?include=` parameter:

- `owner` -- the user who owns the listing

This avoids a separate API call to fetch the owner. See the [JSON:API guide](../guides/jsonapi.md#compound-documents-sideloading) for details on how compound documents work.

### Listing Activation

Use the listing activation endpoint to enable or disable price syncing for a listing.

When enabling a listing, the activation payload includes `enabled` and can also include pricing values such as `base-price` and `min-price`. Newly synced listings are created disabled, so this is the endpoint used in the final step of the [partner onboarding flow](../getting-started/partners.md#step-7-enable-a-listing).

See the interactive schema and examples in the [Swagger UI](/api/v1/docs/#/Listings).

### Listing Reservations Refresh

Use the listing reservations refresh endpoint to enqueue an asynchronous reservations
sync for a listing's primary channel listing.

This endpoint returns immediately with a `202 Accepted` response after the job has
been queued.

### Listing Customizations

Use the [Listing Customizations](listing-customizations.md) endpoints to retrieve and
update pricing and stay-rule settings for a specific listing.

Supported customization types include:

- `min-stays`
- `extra-guest-fees`
- `max-stays`
- `min-max-prices`
- `time-based-adjustments`
- `checkin-checkout-days`

---

## Listing Customizations

| Endpoint | Method | Scope | Description |
| -------- | ------ | ----- | ----------- |
| `/api/v1/listings/<id>/customizations/` | GET | `listings:read` | Get all customizations for a listing in one response |
| `/api/v1/listings/<id>/customizations/base-price/` | GET | `listings:read` | Get base price customization for a listing |
| `/api/v1/listings/<id>/customizations/base-price/` | PATCH | `listings:write` | Update base price customization for a listing |
| `/api/v1/listings/<id>/customizations/min-stays/` | GET | `listings:read` | Get min-stays customization for a listing |
| `/api/v1/listings/<id>/customizations/min-stays/` | PATCH | `listings:write` | Update min-stays customization for a listing |
| `/api/v1/listings/<id>/customizations/extra-guest-fees/` | GET | `listings:read` | Get extra guest fee customization for a listing |
| `/api/v1/listings/<id>/customizations/extra-guest-fees/` | PATCH | `listings:write` | Update extra guest fee customization for a listing |
| `/api/v1/listings/<id>/customizations/max-stays/` | GET | `listings:read` | Get max-stays customization for a listing |
| `/api/v1/listings/<id>/customizations/max-stays/` | PATCH | `listings:write` | Update max-stays customization for a listing |
| `/api/v1/listings/<id>/customizations/min-max-prices/` | GET | `listings:read` | Get min/max prices customization for a listing |
| `/api/v1/listings/<id>/customizations/min-max-prices/` | PATCH | `listings:write` | Update min/max prices customization for a listing |
| `/api/v1/listings/<id>/customizations/time-based-adjustments/` | GET | `listings:read` | Get time-based adjustments customization for a listing |
| `/api/v1/listings/<id>/customizations/time-based-adjustments/` | PATCH | `listings:write` | Update time-based adjustments customization for a listing |
| `/api/v1/listings/<id>/customizations/checkin-checkout-days/` | GET | `listings:read` | Get checkin/checkout days customization for a listing |
| `/api/v1/listings/<id>/customizations/checkin-checkout-days/` | PATCH | `listings:write` | Update checkin/checkout days customization for a listing |

!!! tip "Try it out"
    Explore schemas and live requests in the [Swagger UI](/api/v1/docs/#/Customizations).

### Overview

Listing customization endpoints expose the mutable pricing and stay-rule settings that
apply to a specific listing. These routes use a stable `customizations/` namespace so
new customization types can be added later without changing the API version.

These endpoints are listing-scoped. A future release may add a separate namespace for
global default customizations.

---

### Supported Customization Types

- `base-price`: the listing's nightly base price.
- `min-stays`: minimum-night rules for annual, seasonal, day-of-week, lead-time, and gap-fill behavior.
- `extra-guest-fees`: per-guest fees that apply when a reservation exceeds the included guest count.
- `max-stays`: maximum-night rules for year-round and seasonal date ranges.
- `min-max-prices`: nightly and monthly floor and ceiling pricing, including weekday and seasonal overrides.
- `time-based-adjustments`: lead-time discounts and premiums, either dynamic or manually configured.
- `checkin-checkout-days`: allowed arrival and departure weekdays, including seasonal restrictions.

---

### Conventions

- Use `GET` to retrieve the current customization state for a listing.
- Use `PATCH` to update only the fields you want to change.
- Request and response bodies follow [JSON:API](../guides/jsonapi.md).
- Field names are dasherized in the HTTP payloads.

---

### All Customizations

`GET /api/v1/listings/<id>/customizations/` returns every supported
customization for a listing in a single `listing-customizations` resource,
so partners can retrieve the full picture without one request per type.

The response is keyed by customization name, and each value is identical to
the `attributes` returned by that customization's own `GET` endpoint:

- `base-price`
- `extra-guest-fees`
- `min-max-prices`
- `min-stays`
- `time-based-adjustments`

To retrieve only a subset, use the JSON:API
[sparse fieldset](https://jsonapi.org/format/#fetching-sparse-fieldsets)
query parameter `fields[listing-customizations]` with a comma-separated
list of customization names. For example:

```text
GET /api/v1/listings/123/customizations/?fields[listing-customizations]=base-price,min-stays
```

returns only the `base-price` and `min-stays` customizations. This endpoint
is read-only; continue to use the individual `customizations/<type>/`
endpoints with `PATCH` to make changes.

---

### Base Price

Use `base-price` to set the listing's nightly base price. The base price is the
anchor used by Beyond Pricing's algorithm; minimum and maximum prices, seasonal
rules, and time-based adjustments are all applied relative to it.

The payload includes:

- `base-price`: the nightly base price for the listing. Must be at least `10`.

---

### Extra Guest Fees

Use `extra-guest-fees` to charge an additional fee when a reservation exceeds the
number of guests included in the standard rate.

If no extra guest fee is configured, the standard price applies.

The payload includes:

- `extra-guest-fee`: the fee charged for each guest above the threshold.
- `extra-guest-threshold`: the number of guests included before the fee applies.

---

### Minimum Stays

Use `min-stays` to manage minimum-night requirements for a listing. This
customization supports a combination of year-round defaults, weekday overrides,
seasonal rules, lead-time rules, and gap-fill behavior.

Minimum stays are applied in the following order:

1. Seasonal Time-Based Minimum Stays
2. Time-Based Minimum Stays
3. Seasonal Minimum Stays
4. Day of Week Minimum Stays
5. Annual Minimum Stays

Gap fills apply across minimum stay rules to help protect short orphan gaps in
availability.

#### Annual Minimum Stay

Annual Minimum Stay sets the default minimum stay across all dates except where
overridden by day-of-week or seasonal rules. We recommend setting this to the
shortest minimum stay you would accept during lower-demand periods.

If the annual minimum stay is left blank, it defaults to a 1-night stay.

#### Annual Minimum Stays By Day Of Week

Day-of-week minimum stays take precedence over the annual minimum stay. Days left
blank inherit the annual minimum stay value.

#### Seasonal Minimum Stays By Day Of Week

Seasonal weekday minimum stays override annual rules and annual weekday rules for
their date range. They are still superseded by seasonal minimum stays, time-based
minimum stays, and gap-fill rules.

#### Seasonal Minimum Stays

Seasonal minimum stays let you create date-range specific rules for holidays,
events, or other periods of predictable demand. These rules override annual and
day-of-week minimum stays, but time-based minimum stays and gap fills still take
precedence.

#### Time-Based Minimum Stays

Time-based minimum stays let you set a minimum stay based on how far away a
booking is from today. These rules take precedence over seasonal minimum stays.

#### Seasonal Time-Based Minimum Stays

Seasonal time-based minimum stays let you tailor lead-time rules to specific date
ranges so minimum-night requirements can vary by season.

#### Gap Fill Minimum Stays

Gap-fill settings apply minimum-stay protections around short openings between
bookings. The API supports both year-round gap-fill settings and seasonal
gap-fill settings.

---

### Maximum Stays

Use `max-stays` to limit how long a guest can stay. This customization supports:

- `max-stay`: the default maximum allowed stay, in nights.
- `seasonal-max-stays`: date-range-specific maximum stay overrides.

---

### Min/Max Prices

Use `min-max-prices` to define nightly and monthly price floors and ceilings for
a listing.

Minimum prices are applied in this order:

1. `seasonal-prices`
2. `seasonal-day-of-week-min-prices`
3. `day-of-week-min-prices`
4. `min-price`

All minimum prices are honored by any discounting rules configured for the
listing.

#### Annual Minimum and Maximum Prices

Use `min-price` and `max-price` to set default nightly minimum and maximum
prices across all dates, except where a day-of-week or seasonal minimum applies.
We recommend setting `min-price` to the lowest nightly price you would accept at
any time of year.

#### Annual Minimum Price by Day of Week

Use `day-of-week-min-prices` to set year-round nightly minimum prices for
specific weekdays. These values take precedence over `min-price`. Any weekday
left blank uses the `min-price` value.

#### Seasonal Minimum Prices by Day of Week

Use `seasonal-day-of-week-min-prices` to set weekday-specific nightly minimum
prices for a defined date range. These values take precedence over
`day-of-week-min-prices`, but are superseded by `seasonal-prices`.

#### Seasonal Minimum and Maximum Prices

Use `seasonal-prices` to set nightly minimum and maximum prices for specific
date ranges, such as holidays or events. We do not recommend limiting our
recommended prices by setting a maximum price.

---

### Time-Based Adjustments

The `time-based-adjustments` customization supports two operating modes:
dynamic time-based adjustments and manual time-based adjustments.

#### Dynamic Time-Based Adjustments

Dynamic Time-Based Adjustments is a core algorithm feature. When enabled, Beyond
updates the listing's discount and premium rules based on the latest booking
trends for the market and property type. Keeping this enabled ensures pricing
rules are optimized daily to maximize revenue.

When dynamic time-based adjustments is enabled, the user can choose one of the
available optimization tiers:

- `revenue`
- `occupancy`
- `rate`

`revenue` is the default and recommended option.

#### Manual Annual And Seasonal Adjustments

When dynamic time-based adjustments is disabled, the user can configure manual
annual and seasonal time-based adjustments.

#### Annual Time-Based Adjustments

Annual time-based adjustments help shape pricing across the full booking window.
Discounting dates that are coming up soon can help improve short-term occupancy.
Increasing prices for dates far in the future can help achieve higher ADRs and
avoid booking those dates too quickly.

We recommend always including an increase for dates more than 270 days
(9 months) in the future.

#### Seasonal Time-Based Adjustments

Seasonal time-based adjustments let users tailor discounts and premiums to
seasonal demand patterns. For example, you may discount more aggressively in
low season and less in high season.

Annual adjustments are pre-loaded for easy customization. Users can adjust the
rules as needed and save them to create a seasonal setting.

---

### Check-In/Checkout Days

Use `checkin-checkout-days` to limit which weekdays guests may arrive or depart.

This customization supports:

- `checkin-days`: allowed arrival weekdays year-round.
- `checkout-days`: allowed departure weekdays year-round.
- `seasonal-checkin-checkout-days`: date-range-specific arrival and departure rules.
- `changeover-days-gap-fill-enabled`: whether annual or seasonal changeover-day
  restrictions can be overridden to keep short orphan gaps bookable, while still
  respecting the minimum stay for the period.

---

## Calendar

| Endpoint | Method | Scope | Description |
|----------|--------|-------|-------------|
| `/api/v1/listings/<id>/calendar/` | GET | `listings:read` | Get calendar data for a listing |

!!! tip "Try it out"
    Explore parameters, schemas, and live requests in the [Swagger UI](/api/v1/docs/#/Listings).

### Get Listing Calendar

Returns daily calendar data for a listing, including pricing, availability, and pricing factors. Filter by date range using `filter[start-date]` and `filter[end-date]` (defaults to today through today + 365 days).

Calendar price fields are returned in the currency configured for the user who owns the listing.

#### Availability Values

| Value | Description |
|-------|-------------|
| `available` | The date is available to be booked |
| `booked` | The date has a corresponding reservation |
| `blocked` | The date is blocked off for a hold |
| `unavailable` | The date cannot be booked for an unknown reason |

#### Price Fields

| Field | Description |
|-------|-------------|
| `price` | Beyond Pricing suggested price (shown in the BP dashboard) |
| `price-modeled` | Price modeled by Beyond Pricing's algorithm |
| `price-posted` | Last price posted to the channel/PMS |

!!! tip
    Use `price-posted` when you need the price that will actually be quoted if a booking is made. Fall back to `price` if `price-posted` is `null`.

---

## Compsets

`GET /api/v1/listings/<id>/compset/`

- Scope: `compsets:read`
- Description: Get the precomputed competitive set for a listing.

!!! tip "Try it out"
    Explore schemas and make live requests in the [Swagger UI](/api/v1/docs/#/Listings).

### What is a compset?

A **compset** (Competitive Set) is a group of comparable nearby listings used
to benchmark a property's pricing and performance. Beyond computes one daily
for every connected listing using haversine geo-distance plus Gower similarity
over bedrooms, bathrooms, room/property type, and market percentile.

### Get Compset for a Listing

Returns the precomputed compset for a listing's primary channel listing,
ordered by rank ascending (rank 1 = most similar). Each member carries enough
property attributes that a partner can render a side-by-side comparison in a
single round trip.

Returns **404** when no compset has been computed yet (for example, a newly
connected listing where the daily pipeline has not landed).

#### Optional enrichments — `?include=`

Comma-separated, repeatable:

- `median_curve` — adds the daily median nightly price across compset members
  for the requested date range. Combine with `start_date` and `end_date`
  (defaults: today → today + 90 days, max 180-day range). Heavier query —
  partners running real-time UIs should cache results.
- `revpar` — adds the listing's RevPAN compared to the compset's RevPAN
  (`perf-vs-compset` ratio). Returned with `available: false` if no
  comparison snapshot exists yet for this channel listing.

#### Notable Fields

- `members[].rank` — 1-based ordering; rank 1 is closest to the base listing.
- `members[].distance` — Composite Gower similarity (lower = more similar,
  dimensionless).
- `members[].haversine-km` — Great-circle distance to the base listing, in
  kilometers.
- `members[].market-percentile-tag` — Coarse pricing tier within the market
  (`market_p25`, `market_p50`, `market_p75`).
- `source` — The base listing's features that anchored the similarity search.
- `computed-at` — When the precomputed row was last refreshed by Beyond's
  daily pipeline.

### Use cases

Three scenarios partners can build on top of a single call to this endpoint:

1. **Market pricing pacing.** Combine `members[]` with the opt-in
   `median-price-curve.points[]` to chart own nightly rates vs. the local
   compset median over the next 90 days — gaps where the operator is
   materially above/below the market are immediately visible.
2. **Competitor comparison.** Use `source` and `members[]` together to render
   a comparison table (bedrooms, bathrooms, star rating, amenities, distance)
   and highlight where the base listing is below the cluster.
3. **Performance KPI.** Use the opt-in `revpar` block to surface a single
   gauge (e.g. `perf-vs-compset < 0.9` red, `0.9–1.1` amber, `> 1.1` green)
   per listing, rolled up to a portfolio scorecard.

### Compound documents

This endpoint does not currently sideload via JSON:API `?include=`
relationships. The `include=` query parameter is reserved for the opt-in
enrichments described above; partners should not expect JSON:API sideload
semantics here.

### Errors

| Status | Cause |
| ------ | ----- |
| `400`  | Invalid `include` value, malformed `start_date`/`end_date`, or range > 180 days. |
| `401`  | Missing or invalid bearer token. |
| `403`  | Token does not have the `compsets:read` scope, or partner credential lacks privileges on the listing. |
| `404`  | Listing not owned by the application, has no primary channel listing, or has no precomputed compset row yet. |

---

## Recommendations

`GET /api/v1/listings/<id>/recommendations/`

- Scope: `listings:read`
- Description: List recommendations for a listing

!!! tip "Try it out"
    Explore schemas and make live requests in the [Swagger UI](/api/v1/docs/#/Listings).

### List Recommendations

Returns recommendations for a listing owned by the authenticated application.

Recommendations are generated periodically using the newest data available for
the listing and its market. They are designed to suggest pricing updates as
market conditions change over time.

The current recommendations endpoint is primarily focused on base price changes.
When a recommendation is available, the response includes the suggested base
price and the recommendation status for that listing.

By default, Beyond accepts base price changes automatically. Partners can use
this endpoint to review the latest recommendation state and understand whether a
base price recommendation is pending, approved, rejected, expired, or already
applied automatically.

#### Notable Fields

- `status`: Current recommendation lifecycle status, such as `suggestion_completed`, `approved`, or `rejected`
- `category`: Recommendation category. For partners, the most relevant category is `base_price`.
- `suggested-base-price`: Suggested base price derived from the latest listing and market data.
- `status`: Indicates whether the recommendation is pending review, accepted, rejected, expired, or otherwise no longer actionable.
- `created-at`: Timestamp for when the recommendation record was created.

---

## Users

| Endpoint              | Method   | Scope        | Token Tier     | Description                     |
| --------------------- | -------- | ------------ | -------------- | ------------------------------- |
| `/api/v1/users/`      | GET      | `user:read`  | Both           | List all users (paginated)      |
| `/api/v1/users/`      | POST     | `user:write` | App-level only | Create a new user               |
| `/api/v1/users/<id>/credentials/` | GET | `user:read` | Both | List credentials for a user |
| `/api/v1/users/<id>/` | DELETE   | `user:write` | App-level only | Delete a user                   |

!!! tip "Try it out"
    Explore parameters, schemas, and live requests in the [Swagger UI](/api/v1/docs/#/Users).

### List Users

Returns a paginated list of all users owned by the authenticated application. Supports [pagination and sorting](../guides/jsonapi.md#pagination) by `created_at`.

!!! note "User-scoped tokens"
    When using a user-scoped token, this endpoint returns only the user the token is bound to.

### Create User

Create a new user managed by your application. Users created via this endpoint are managed by the OAuth2 application and cannot log in directly (password is randomly generated).

If a user with the same email already exists, the API returns `409 Conflict`.

!!! warning "App-level token required"
    This endpoint requires an app-level token. Requests with a user-scoped token will receive `403 Forbidden`.

#### Optional Attributes

You can also provide these optional attributes when creating a user:

- `locale`: Optional. Defaults to `en`. Uses a supported [BCP 47](https://www.rfc-editor.org/rfc/bcp/bcp47.txt) language tag. Supported values are `en`, `en-GB`, `en-AU`, `en-CA`, `ja`, `fr`, `pt`, `de`, `es`, and `it`. This locale is used in messages that explain the reason for price changes in the booking review endpoint (not yet available).

#### Example

```json
{
  "data": {
    "type": "users",
    "attributes": {
      "first-name": "John",
      "last-name": "Doe",
      "email": "john@example.com",
      "locale": "en-GB"
    }
  }
}
```

### List User Credentials

Returns the login credentials that are visible for the requested user.

- App-level tokens can list all credentials for the user.
- User-scoped tokens default to the bound credential. Admin credentials can list all credentials for that user; non-admin credentials only see themselves.
- Each credential object has its own `id`. Use that `credential_id` when requesting a user-scoped token that must enforce one credential's visibility and grants.

### Delete User

Soft-delete a user managed by your application. This anonymizes the user's email, disables all enabled listings, removes managed accounts, and marks the user as deleted.

Returns `204 No Content` on success.

!!! warning "App-level token required"
    This endpoint requires an app-level token. Requests with a user-scoped token will receive `403 Forbidden`.

---

## Accounts

Accounts represent channel connections (e.g., Airbnb, Hostaway, or other PMS accounts) linked to a user.

| Endpoint | Method | Scope | Description |
| -------- | ------ | ----- | ----------- |
| `/api/v1/users/<user_id>/accounts/` | GET | `user:read` | List accounts for a user |
| `/api/v1/users/<user_id>/accounts/` | POST | `user:write` | Add an account for a user |
| `/api/v1/users/<user_id>/accounts/<account_id>/refresh/` | POST | `user:write` | Queue a full refresh for an account |
| `/api/v1/users/<user_id>/accounts/<account_id>/` | DELETE | `user:write` | Delete an account |

!!! tip "Try it out"
    Explore parameters, schemas, supported channels with their required credentials, and live requests in the [Swagger UI](/api/v1/docs/#/Accounts).

### List Accounts

Returns a paginated list of accounts (channel connections) for the specified user.

Use this endpoint to monitor the background import that starts after an account is created. The response includes `sync-status`, which can be used to detect whether the account is still `queued`, `in_progress`, or `completed`.

For the end-to-end workflow, see the [partner onboarding flow](../getting-started/partners.md#end-to-end-onboarding-flow).

### Add Account

Add a channel connection (account) for a user. Each channel requires specific credentials (API key, client ID/secret, JWT, etc.). The Swagger UI documents the complete list of 50+ supported channels and the credentials each one requires.

User-scoped tokens may call this endpoint, but the bound credential must have global `edit` or `admin` permissions.

If channel validation or authentication fails, the API returns `422 Unprocessable Entity` with a `meta.channel` field identifying which channel failed and a `meta.code` field with the error type.

Creating an account starts a background sync that imports reservation history and creates listings for the connected account.

### Refresh Account

Queue a full listings and reservations refresh for an existing account.

User-scoped tokens may call this endpoint, but the bound credential must have global `edit` or `admin` permissions.

Use this endpoint when you want to manually trigger another import after the account has already been connected, for example after fixing channel-side data issues or when you want to force a fresh sync without reconnecting the account.

The API returns `202 Accepted` after the refresh job is queued. The refresh runs asynchronously, so use [List Accounts](#list-accounts) to poll the account's `sync-status`.

This refresh is prioritized ahead of normal queued work and replaces an existing pending account refresh that has not started yet.

Use the optional `recent_sync_threshold_minutes` query parameter to control how recently synced listings are skipped during the refresh. The threshold unit is minutes.

- If omitted, the default is `60`.
- If set to `0`, recently synced listings are not skipped and the refresh includes all listings.

On success, the API returns a JSON:API document with `meta` describing the accepted refresh request and `links.related` pointing to the account resource you can poll for `sync-status`.

### Delete Account

Remove an account (channel connection) for the specified user.

User-scoped tokens may call this endpoint, but the bound credential must have global `edit` or `admin` permissions.

Returns `204 No Content` on success.

---

# Release Notes

This page documents changes to the Partners API. Entries are organized by API
version, then by date (newest first), and grouped into categories.

For details on what constitutes a breaking vs. non-breaking change, see
[API Versioning](guides/versioning.md).

## v1

No release notes yet -- this section will be updated as changes are made
to the API. Check back soon!