openapi: 3.1.1
info:
title: Beyond Pricing Public Accounts API
version: 2.0.0
description: Bearer-protected API for third-party integrations. Supports OAuth2 client credentials and personal access tokens. Follows JSON:API specification.
tags:
- name: Accounts
paths:
/api/v1/users/{user_id}/accounts/:
get:
operationId: list_accounts
description: 'Return a paginated list of accounts (channel connections) for the specified user.
## Response Codes
- **200**: Success
- **401**: Unauthorized - invalid or missing OAuth2 token
- **403**: Forbidden - insufficient scope
- **404**: Not found - user not found'
summary: List accounts for a user
parameters:
- in: path
name: user_id
schema:
type: integer
required: true
- name: page[number]
required: false
in: query
description: A page number within the paginated result set.
schema:
type: integer
- name: page[size]
required: false
in: query
description: Number of results to return per page.
schema:
type: integer
tags:
- Accounts
security:
- oauth2:
- user:read
- personalAccessToken: []
responses:
'200':
content:
application/vnd.api+json:
schema:
$ref: '#/components/schemas/PaginatedAccountList'
description: ''
'401':
description: Unauthorized - invalid or missing bearer token
'403':
description: Forbidden
'404':
description: Not found - resource does not exist
'429':
description: Rate limit exceeded
'500':
description: Internal server error
post:
operationId: create_account
description: 'Add a channel connection (account) for a user.
Each channel requires specific credentials.
## Supported Channels and Required Credentials
| Channel | Required Fields | Optional Fields |
|---------|-----------------|-----------------|
| `airbnb` | `email` | `password`, `device_id` |
| `airbnb_partner` | `access_token` | `refresh_token`, `email` |
| `avantio` | `provider_id`, `username`, `password` | - |
| `barefoot_direct` | `account_id` | `web_book_reztypeid` |
| `beds24` | `account_id` | - |
| `best_beach` | `api_key` | - |
| `booking_connect` | `email`, `password` | `device_id` |
| `booking_experts` | `authorization_code` | - |
| `bookingsync` | `authorization_code`, `redirect_uri` | - |
| `brightside` | `subdomain`, `api_key` | - |
| `ciirus` | `company_id` | `booking_id`, `booking_secret` |
| `cloudbeds` | `authorization_code` | - |
| `direct` | `organization_id` | - |
| `elina` | `password` | - |
| `escapia` | `email`, `domain`, `password`, `pm_id` | `evrn_api_user`, `evrn_api_password` |
| `fantasticstay` | `api_key` | - |
| `gfh` | `api_key` | - |
| `guesty` | `jwt` | `booking_id`, `booking_secret` |
| `homeaway` | `authorization_code` | - |
| `homhero` | `reference` | - |
| `homhero_staging` | `reference` | - |
| `hospitable` | `authorization_code`, `client_id`, `client_secret`, `redirect_uri` | - |
| `hostaway` | `client_id`, `client_secret` | - |
| `hostfully` | `agency_id` | `authorization_code` |
| `hosthub` | `api_key` | - |
| `hostify` | `api_key` | - |
| `icnea` | `account_id` | - |
| `igms` | `authorization_code` | - |
| `ipro` | `client_code`, `password`, `domain` | - |
| `janiis` | `organization_id`, `api_key` | - |
| `kigo` | `api_key` | - |
| `kigo_pro` | `authorization_code` | - |
| `kross_booking` | `hotel_id`, `username`, `password` | - |
| `lightmaker` | `company_id` | - |
| `liverez` | `username`, `password`, `security_id` | - |
| `lodgable` | `client_id`, `client_password` | - |
| `lodgify` | `api_key` | - |
| `lodgify_partner` | `api_key` | - |
| `lodgix` | `api_key` | - |
| `loggia` | `email`, `api_key`, `page_id` | - |
| `mews` | `access_token` | - |
| `myvr` | `authorization_code` | - |
| `octorate` | `authorization_code`, `redirect_uri` | - |
| `opera` | `enterprise_id`, `client_id`, `hotel_id`, `client_secret`, `base_url` | - |
| `ownerrez` | `authorization_code`, `redirect_uri` | - |
| `real_time_rental` | `account_id` | - |
| `rentalready` | `authorization_code` | - |
| `rentals_united` | `email` | `password` |
| `resly` | `api_key`, `property_id` | - |
| `rms` | `client_id`, `client_password` | - |
| `secra` | `landlord_no`, `authcode` | - |
| `septeo` | `agency_id` | - |
| `smoobu` | `client_id`, `api_key` | - |
| `stays` | `username`, `password`, `base_url` | - |
| `streamline` | `token_key`, `token_secret` | - |
| `supercontrol` | `client_key` | - |
| `tokeet` | `access_code`, `redirect_uri` | - |
| `track` | `username`, `password`, `subdomain`, `post_key`, `post_secret` | - |
| `travelmob` | `email` | `password` |
| `uplisting` | `api_key` | - |
| `villas365` | `account_id`, `owner_token`, `key`, `password` | - |
| `vrbo` | `email` | `password` |
| `vreasy` | `api_key` | - |
| `vrm` | `client_code` | - |
| `zeevou` | `username`, `secret` | - |
| `zeevou_direct` | `authorization_code` | - |
_Dynamic/custom PMS channels require `api_key` and optionally `base_url`._
## Response Codes
- **201**: Account created successfully
- **400**: Validation error - invalid input data
- **401**: Unauthorized - invalid or missing OAuth2 token
- **403**: Forbidden - insufficient scope
- **404**: Not found - user not found
- **422**: Channel validation/authentication failed
- **502**: Channel error (external service issue)'
summary: Add an account for a user
parameters:
- in: path
name: user_id
schema:
type: integer
required: true
tags:
- Accounts
requestBody:
content:
application/vnd.api+json:
schema:
$ref: '#/components/schemas/AccountCreateRequest'
examples:
APIKeyChannel(Hostify):
value:
data:
type: accounts
attributes:
channel: hostify
credentials:
api_key: your-api-key-here
summary: Add an API key channel
ClientID/SecretChannel(Hostaway):
value:
data:
type: accounts
attributes:
channel: hostaway
credentials:
client_id: '12345'
client_secret: abcdef123456
summary: Add a client ID/secret channel
JWTChannel(Guesty):
value:
data:
type: accounts
attributes:
channel: guesty
credentials:
jwt: your-jwt-token-here
summary: Add a JWT-based channel
required: true
security:
- oauth2:
- user:write
- personalAccessToken: []
responses:
'201':
content:
application/vnd.api+json:
schema:
$ref: '#/components/schemas/AccountResponse'
description: ''
'400':
description: Validation error
'401':
description: Unauthorized - invalid or missing bearer token
'403':
description: Forbidden
'404':
description: Not found - resource does not exist
'422':
description: Unprocessable entity
'429':
description: Rate limit exceeded
'500':
description: Internal server error
'502':
description: Channel error
/api/v1/users/{user_id}/accounts/{account_id}/:
get:
operationId: get_account
description: 'Retrieve a single account (channel connection) for the specified user.
## Response Codes
- **200**: Success
- **401**: Unauthorized - invalid or missing OAuth2 token
- **403**: Forbidden - insufficient scope
- **404**: Not found - user or account not found'
summary: Get an account for a user
parameters:
- in: path
name: user_id
schema:
type: integer
required: true
- in: path
name: account_id
schema:
type: integer
required: true
tags:
- Accounts
security:
- oauth2:
- user:read
- personalAccessToken: []
responses:
'200':
content:
application/vnd.api+json:
schema:
$ref: '#/components/schemas/AccountResponse'
description: ''
'401':
description: Unauthorized - invalid or missing bearer token
'403':
description: Forbidden
'404':
description: Not found - resource does not exist
'429':
description: Rate limit exceeded
'500':
description: Internal server error
delete:
operationId: delete_account
description: 'Remove a channel connection (account) for a user. This unregisters webhooks and soft-deletes the account.
## Response Codes
- **204**: Successfully deleted
- **401**: Unauthorized - invalid or missing OAuth2 token
- **403**: Forbidden - insufficient scope
- **404**: Not found - user or account not found'
summary: Delete an account for a user
parameters:
- in: path
name: user_id
schema:
type: integer
required: true
- in: path
name: account_id
schema:
type: integer
required: true
tags:
- Accounts
security:
- oauth2:
- user:write
- personalAccessToken: []
responses:
'204':
description: No content
'401':
description: Unauthorized - invalid or missing bearer token
'403':
description: Forbidden
'404':
description: Not found - resource does not exist
'429':
description: Rate limit exceeded
'500':
description: Internal server error
/api/v1/users/{user_id}/accounts/{account_id}/refresh/:
post:
operationId: refresh_account
description: 'Queue a full listings and reservations refresh for the specified account.
This endpoint enqueues an asynchronous `sync_all` job and returns immediately.
Use `recent_sync_threshold_minutes` to control how recently synced listings are skipped. The threshold unit is minutes. When set to `0`, the refresh includes all listings.
If you have registered a webhook, an `account.refreshed` event is delivered once the listing sync reaches a terminal state, so you do not have to poll. Reservations are refreshed by separate background jobs and are still in flight when that event fires.
## Response Codes
- **400**: Validation error - invalid query parameter value
- **202**: Refresh accepted and queued
- **401**: Unauthorized - invalid or missing OAuth2 token
- **403**: Forbidden - insufficient scope
- **404**: Not found - user or account not found
- **409**: Conflict - a refresh is already in progress; retry shortly'
summary: Refresh an account for a user
parameters:
- in: path
name: user_id
schema:
type: integer
required: true
- in: path
name: account_id
schema:
type: integer
required: true
- in: query
name: recent_sync_threshold_minutes
schema:
type: integer
description: Skip listings synced within the last N minutes. Defaults to 60. Set to 0 to disable this optimization and refresh all listings.
tags:
- Accounts
security:
- oauth2:
- user:write
- personalAccessToken: []
responses:
'400':
description: Validation error
'202':
description: Refresh accepted
'401':
description: Unauthorized - invalid or missing bearer token
'403':
description: Forbidden
'404':
description: Not found - resource does not exist
'409':
description: Conflict - a refresh is already in progress for this account
'429':
description: Rate limit exceeded
'500':
description: Internal server error
components:
schemas:
AccountResourceTypeEnum:
type: string
enum:
- accounts
StateEnum:
enum:
- queued
- in_progress
- completed
- unknown
type: string
description: '* `queued` - queued
* `in_progress` - in_progress
* `completed` - completed
* `unknown` - unknown'
AccountCreateRequest:
type: object
properties:
data:
type: object
required:
- type
additionalProperties: false
properties:
type:
type: string
description: The [type](https://jsonapi.org/format/#document-resource-object-identification) member is used to describe resource objects that share common attributes and relationships.
enum:
- accounts
attributes:
type: object
properties:
channel:
type: string
minLength: 1
description: 'Channel identifier (e.g., ''hostify'', ''hostaway''). Supported channels: airbnb, airbnb_partner, avantio, barefoot_direct, beds24, best_beach, booking_connect, booking_experts, bookingsync, brightside, ciirus, cloudbeds, direct, elina, escapia, fake, fake_2, fake_pms, fantasticstay, gfh, guesty, homeaway, homhero, homhero_staging, hospitable, hostaway, hostfully, hosthub, hostify, icnea, igms, ipro, janiis, kigo, kigo_pro, kross_booking, lightmaker, liverez, lodgable, lodgify, lodgify_partner, lodgix, loggia, mews, myvr, octorate, opera, ownerrez, real_time_rental, rentalready, rentals_united, resly, rms, secra, septeo, smoobu, stays, streamline, supercontrol, tokeet, track, travelmob, uplisting, villas365, vrbo, vreasy, vrm, zeevou, zeevou_direct'
credentials:
type: object
additionalProperties: {}
description: Credentials for the specified channel
required:
- channel
- credentials
required:
- data
PaginatedAccountList:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/Account'
links:
type: object
description: Pagination links for the primary data. These keys MUST be used for pagination links; each MUST be omitted or `null` when that link is unavailable. Values follow JSON:API link rules (URI string, link object, or null). https://jsonapi.org/format/#fetching-pagination https://jsonapi.org/format/#document-links
properties:
first:
type:
- string
- 'null'
format: uri
example: http://example.com/articles?page%5Bsize%5D=10&page%5Bnumber%5D=1
description: 'The first page of data. A JSON:API link: URI string, link object, or null. See https://jsonapi.org/format/#document-links.'
last:
type:
- string
- 'null'
format: uri
example: http://example.com/articles?page%5Bsize%5D=10&page%5Bnumber%5D=9
description: 'The last page of data. A JSON:API link: URI string, link object, or null. See https://jsonapi.org/format/#document-links.'
prev:
type:
- string
- 'null'
format: uri
example: http://example.com/articles?page%5Bsize%5D=10&page%5Bnumber%5D=1
description: 'The previous page of data. A JSON:API link: URI string, link object, or null. See https://jsonapi.org/format/#document-links.'
next:
type:
- string
- 'null'
format: uri
example: http://example.com/articles?page%5Bsize%5D=10&page%5Bnumber%5D=3
description: 'The next page of data. A JSON:API link: URI string, link object, or null. See https://jsonapi.org/format/#document-links.'
additionalProperties: false
meta:
type: object
description: Non-standard meta-information. Page-number stacks often nest totals under `pagination`. JSON:API does not define these keys. Servers MAY add other `meta` members; this schema documents the usual djangorestframework-json-api shape only. https://jsonapi.org/format/#document-meta
properties:
pagination:
type: object
description: Typical page-number metadata from djangorestframework-json-api; not mandated by JSON:API.
properties:
count:
type: integer
minimum: 0
example: 42
description: Total number of resources across all pages.
page:
type: integer
minimum: 1
example: 2
description: Current page number (1-based).
pages:
type: integer
minimum: 0
example: 5
description: Total number of pages.
additionalProperties: false
additionalProperties: false
required:
- data
AccountResponse:
type: object
properties:
data:
$ref: '#/components/schemas/Account'
required:
- data
AccountSyncStatus:
type: object
description: 'Serializer for the account sync status.
Receives the full ManagedAccount instance via ``source="*"`` and derives
the sync state from the pending sync job and ``listings_synced_at``.'
properties:
state:
allOf:
- $ref: '#/components/schemas/StateEnum'
description: 'Current sync state: queued, in_progress, completed, or unknown
* `queued` - queued
* `in_progress` - in_progress
* `completed` - completed
* `unknown` - unknown'
last-successful-sync-at:
type:
- string
- 'null'
format: date-time
description: Timestamp of the last successful listing sync, or null if never synced
required:
- last-successful-sync-at
- state
Account:
type: object
required:
- type
- id
additionalProperties: false
properties:
type:
allOf:
- $ref: '#/components/schemas/AccountResourceTypeEnum'
description: The [type](https://jsonapi.org/format/#document-resource-object-identification) member is used to describe resource objects that share common attributes and relationships.
id: {}
attributes:
type: object
properties:
user-id:
type: integer
description: ID of the user who owns this account
channel:
type: string
description: Channel identifier (e.g., 'airbnb', 'guesty')
channel-id:
type: string
description: Account identifier on the channel side
channel-display-id:
type:
- string
- 'null'
description: Human-readable display ID
label:
type: string
description: Human-readable channel label
email:
type:
- string
- 'null'
description: Email associated with this account
valid:
type: boolean
description: Whether the account credentials are currently valid
nb-enabled-listings:
type: integer
readOnly: true
description: Number of enabled listings in this account
nb-listings:
type: integer
readOnly: true
description: Total number of listings in this account
is-pms:
type: boolean
readOnly: true
description: Whether this channel is a property management system
sync-status:
allOf:
- $ref: '#/components/schemas/AccountSyncStatus'
readOnly: true
description: Status of the account sync process started after account connection
required:
- id
- user-id
- channel
- channel-id
- channel-display-id
- label
- email
- valid
securitySchemes:
oauth2:
type: oauth2
description: Use OAuth2 client credentials to mint an application token, or add `user_id` and optional `credential_id` in the Authorize dialog to request a user- or credential-scoped token.
flows:
clientCredentials:
tokenUrl: /o/token/
refreshUrl: /o/token/
scopes:
listings:read: Read listings
listings:write: Modify listings
reservations:read: Read reservations
accounts:read: Read account information
user:read: Read user information
user:write: Create and modify users
insights:read: Read market insights
compsets:read: Read competitive set data
neyoba:ask: Ask Neyoba
personalAccessToken:
type: http
scheme: bearer
bearerFormat: PersonalAccessToken
description: Paste a `bpat_...` personal access token. PATs use the same Bearer header as OAuth2 tokens and must still include the runtime-required scopes for each endpoint, even though OpenAPI cannot encode scopes for non-OAuth bearer schemes.