Listing Created¶
Sent when a new listing owned by one of your users is fully set up — and,
with status: "failed", when an attempt to set one up fails. Use it to learn
about new listings the moment they are ready, and about listings that need
attention before they can be created, instead of polling the
Listings endpoint.
| Event type | listing.created |
| Resource type | listing-created-events |
| Triggered by | A new listing finishing setup — its initial base price is computed — a previously removed listing coming back, or a sync attempt failing to create one |
When It Fires¶
A listing is not ready to price against the moment its row is created: Beyond
first clusters it, resolves its market, imports its calendar, and computes an
initial base price for it. With status: "succeeded", this event fires at
that last step — the first time the listing's base price is computed — so when
you receive it the listing is fully set up and its base-price is populated.
It fires once on that first pricing, and again if the listing is ever removed
and later comes back — see Restored listings below.
Treat the event as an upsert rather than an insert. Later base-price changes
are delivered as listing.base_price_changed instead,
and the listing later leaving or re-entering the fully-priceable state is
announced via
listing.in_active_market_changed.
Restored Listings¶
Deleting a managed account removes its listings from your inventory, but the listings are kept: re-creating the account with the same channel credentials brings the same listings back, at the same listing ids. The same is true of a listing that disappears from the channel long enough to be removed and later reappears.
Because the listing already has its base price, the first-pricing milestone
above cannot fire a second time — so the return is announced with a
status: "succeeded" event carrying restored: true. Everything else about
the payload matches a first-time creation, including created-at, which is
still the listing's original creation timestamp; meta.sent-at is when it
came back.
The restored attribute is omitted entirely for a first-time creation, so
a payload that never had it is unchanged.
With status: "failed", the event fires when a sync attempt fails to create a
listing that has not been set up yet — for example a property whose channel
setup is incomplete (missing rates, misconfigured rate plans) or an unexpected
error while importing it. The error attribute carries a human-readable
description. Unlike the succeeded case, a failed event fires on every
failing sync attempt: a listing whose setup stays broken repeats the event on
each re-sync (accounts re-sync daily), until it is either fixed — at which
point a status: "succeeded" event follows — or removed.
The webhook is delivered to the OAuth2 application that owns the listing's user
(User.owner_application). Listings owned by ordinary, non-API users have no
owning application and produce no event.
First base price, then changes
listing.created carries the listing's initial base price. To keep
tracking that value over time, subscribe to
listing.base_price_changed, which fires on every
subsequent change.
Payload¶
{
"meta": {
"type": "listing.created",
"sent-at": "2026-07-06T12:05:00Z"
},
"data": {
"type": "listing-created-events",
"id": "msg_01J9Z7Q1H4RM8ZW9P3D5C6B7E8",
"attributes": {
"status": "succeeded",
"created-at": "2026-07-06T12:00:00Z",
"title": "Ocean View Retreat",
"currency": "USD",
"base-price": 210,
"channel-listings": [
{ "channel": "airbnb", "channel-id": "external-listing-987" }
]
},
"relationships": {
"listing": {
"data": { "type": "listings", "id": "12345" },
"links": { "related": "https://developers.beyondpricing.com/api/v1/listings/12345/" }
},
"user": {
"data": { "type": "users", "id": "456" },
"links": { "related": "https://developers.beyondpricing.com/api/v1/users/456/" }
},
"account": {
"data": { "type": "accounts", "id": "789" },
"links": { "related": "https://developers.beyondpricing.com/api/v1/users/456/accounts/789/" }
}
}
}
}
The meta envelope is documented in the Payload
Envelope section.
Restored Listing¶
A listing that came back. Identical to the payload above apart from
restored, and the ids are the ones you already know.
{
"meta": {
"type": "listing.created",
"sent-at": "2026-08-01T09:14:00Z"
},
"data": {
"type": "listing-created-events",
"id": "msg_01J9Z7Q1H4RM8ZW9P3D5C6B7EA",
"attributes": {
"status": "succeeded",
"restored": true,
"created-at": "2026-07-06T12:00:00Z",
"title": "Ocean View Retreat",
"currency": "USD",
"base-price": 210,
"channel-listings": [
{ "channel": "airbnb", "channel-id": "external-listing-987" }
]
},
"relationships": {
"listing": {
"data": { "type": "listings", "id": "12345" },
"links": { "related": "https://developers.beyondpricing.com/api/v1/listings/12345/" }
},
"user": {
"data": { "type": "users", "id": "456" },
"links": { "related": "https://developers.beyondpricing.com/api/v1/users/456/" }
},
"account": {
"data": { "type": "accounts", "id": "789" },
"links": { "related": "https://developers.beyondpricing.com/api/v1/users/456/accounts/789/" }
}
}
}
}
Failed Creation Attempt¶
On a failed attempt the listing usually does not exist yet, so the
listing-specific attributes are null and the listing relationship is
omitted; channel-listings and the account/user relationships tell you
which property failed. The meta.sent-at envelope timestamp is when the
failure was observed.
{
"meta": {
"type": "listing.created",
"sent-at": "2026-07-07T12:05:00Z"
},
"data": {
"type": "listing-created-events",
"id": "msg_01J9Z7Q1H4RM8ZW9P3D5C6B7E9",
"attributes": {
"status": "failed",
"error": "Room rates are not configured for this property. Please configure a rate in your PMS.",
"created-at": null,
"title": null,
"currency": null,
"base-price": null,
"channel-listings": [
{ "channel": "mews", "channel-id": "external-listing-987" }
]
},
"relationships": {
"user": {
"data": { "type": "users", "id": "456" },
"links": { "related": "https://developers.beyondpricing.com/api/v1/users/456/" }
},
"account": {
"data": { "type": "accounts", "id": "789" },
"links": { "related": "https://developers.beyondpricing.com/api/v1/users/456/accounts/789/" }
}
}
}
}
data.attributes¶
| Field | Type | Description |
|---|---|---|
status |
string | Outcome of the listing setup: succeeded or failed. |
error |
string | Human-readable description of what went wrong. Present only when status is failed. |
restored |
boolean | true when this listing was previously removed and has now come back (see Restored listings). Omitted entirely for a first-time creation and on a failed attempt. |
created-at |
string (RFC 3339) | null | UTC timestamp when the listing was created. null on a failed attempt where the listing does not exist yet. |
title |
string | null | The listing's title. null on a failed attempt. |
currency |
string | null | ISO 4217 currency code the listing is denominated in. null on a failed attempt. |
base-price |
integer | null | The listing's initial base price, computed by Beyond during setup, in the listing currency — the same whole number the listing resource exposes as base-price (fractional amounts are truncated toward zero). null on a failed attempt. |
channel-listings |
array of objects | The listing's active channel listings — one entry per channel the listing is syndicated to, the same channel-listings exposed on the listing resource. Empty ([]) on the rare listing with no active channel listing. On a failed attempt it holds the single channel listing that could not be set up (empty when the channel is unknown). Each entry has channel (the channel, e.g. airbnb, hostaway) and channel-id (the listing's identifier on the channel side, for correlating the event with your own records). |
data.relationships¶
Every relationship carries a links.related URL you can GET for the current
state of the related resource.
| Relationship | Description |
|---|---|
listing |
The Beyond Pricing master listing that was created ({ "type": "listings", "id": … }). Omitted on a failed attempt where the listing does not exist yet. |
user |
The user who owns the listing ({ "type": "users", "id": … }). |
account |
The managed account the listing's primary channel listing belongs to ({ "type": "accounts", "id": … }). Omitted on the rare listing with no active channel listing. |
Handling Tips¶
- Check
statusfirst. Treatsucceededas "a new listing is ready" — by the time you receive it, the listing endpoint returns its full representation, including thebase-priceshown here. - Follow the
listing.links.relatedURL to fetch the listing's current representation rather than assuming its contents from this event. - Dedupe on
webhook-id(see idempotency) since retries re-use it. That handles retries, not repeats: a re-onboarded listing fires a genuinely new event with a differentwebhook-id. Upsert on thelistingrelationship id rather than inserting, so a listing you already know about is updated instead of duplicated;restored: truetells you when to expect it. - A
failedevent repeats on every failing sync attempt, so expect roughly one per broken property per day until it is fixed. Use thechannel-listings[].channel-id+ theaccountrelationship to group repeats for the same property; surface theerrorto whoever can fix the channel setup. - A
failedevent does not mean the listing never gets created: once the setup issue is fixed, the next sync creates it and asucceededevent follows.
Known Limitations¶
- Failure events cover errors raised while importing a property (channel setup
problems and unexpected sync errors). A listing that imports cleanly but
never gets an initial base price (for example, its market is not yet active,
or it falls outside any pricing cluster) currently produces no event —
neither succeeded nor failed. Both cases surface as
in-active-market: falseon the listing resource; once a listing has a base price, later changes of that attribute are announced vialisting.in_active_market_changed. - Transient infrastructure errors (timeouts, worker restarts) are retried internally and do not fire failure events.