States and events

The lifecycle of each resource, what changes each state and the webhook for each change.

This page shows how each resource changes state. In each diagram, a box is a state and an arrow says what causes the change. The webhook that tells you comes in parentheses. The full list of events is in Receiving webhooks.

  • The three final states never change again. To connect again, create another session.
  • Only a pending session can be revoked. In the other states, DELETE returns 409.
  • The session lasts up to 5 minutes (expiresAt). A Connection granted before that keeps working after the session expires.

Connection

  • active: working. The API renews the access on its own before it expires.
  • error: renewing failed, and we do not know whether the access ended. Ask the Seller to grant access again in a link session.
  • revoked: the access ended. revokedReason says who cut it: client (you) or marketplace. The credential is deleted.
  • Granting access again reuses the Connection. The id is the same, and connection.updated brings the previous state in previousStatus.

Listing: publicationState

The offer's publicationState answers: "has what changed here gone out to the marketplace yet?" The content has its own, right below.

  • never_published: the listing only exists here.
  • dirty: there is a local change that has not been published yet.
  • pending: a publish or removal job is running. When removal on the marketplace finishes well, the Listing is deleted and you get listing.deleted.
  • clean: what is here is what was published or imported.
  • failed: the last publish or removal job failed. You get listing.updated with behavior: "publication_failed" and the jobId; the error is on the Job item. Fix it and publish again.
  • dirty wins over the end of a job. If you edit the listing while the job runs, it ends dirty, because the edit has not been published yet.
  • Reconciling a listing that already exists keeps dirty and pending; in the other states, it becomes clean.

marketplaceStatus (active, paused, closed, scheduled, suppressed, frozen…) is something else: it comes from the marketplace and changes on publishing, import, reconciliation and notifications. With it come the layers buyable, discoverable, pendingRevision and statusReasons, on the Listing and on each binding. When one changes, you receive listing.status_changed with the before and after of the Listing and only of the lines that changed; source tells where it came from (imported, inbound, reconciled, published, or inventory_published). The first import does not emit this event: listing.created already covers it.

linkState is linked when the listing has at least one binding, and unlinked when it has none. It is ambiguous when an import found more than one Variant for some variation and linked none of them; fix it with the bindings PUT.

MarketplaceProduct: publicationState and events

The content has the same states as the offer, but it moves on its own:

  • never_published: the content only exists here. This is how it is born, when you create or clone a Listing.
  • dirty: a PATCH or a replacement of the variations changed the content and it has not been published yet.
  • clean: the content was published or imported.
  • Publishing the Listing publishes the content with it, if it was never published. The content becomes clean. If it had been published and you changed it afterwards, it stays dirty even after you publish the Listing: publishing only the content takes the content publication route.
  • The MarketplaceProduct has no DELETE. Only the publish route talks to the marketplace; it keeps the content pending until the Job ends.

Each change becomes a webhook, marketplace_product.created or marketplace_product.updated. The behavior of updated says what happened:

behaviorWhen
local_updateyou changed the content with PATCH
variants_changedthe variations changed: PUT …/variants, or a bindings PUT that created one
publishedpublishing a Listing confirmed the content on the marketplace
importedan import brought the content in again
publication_requestedyou asked for the content publication; the content becomes pending (carries the jobId)
publication_failedthe content publication failed for good (carries the jobId)
external_changedthe marketplace changed the content and the change reached observed

Job

Each Job has items, one per resource, which go from pending to succeeded or failed only once. The Job types and who creates each one:

typeWho creates itOne item per
connection.syncthe API, when a Connection is born or access is granted againConnection
listing.publishPOST /v1/listings/{listingId}/publishListing
listing.removea DELETE /v1/listings/{listingId} that removes it on the marketplaceListing
listing.importPOST /v1/connections/{id}/listing-importsConnection
listing.reconcilePOST /v1/connections/{id}/listing-reconciliationsConnection
order.importPOST /v1/connections/{id}/order-importsConnection
order.reconcilePOST /v1/connections/{id}/order-reconciliationsConnection
inventory.publishan inventory movement, automatic sync or POST …/inventory-publicationsbinding
marketplace_product.publishPOST /v1/marketplace-products/{marketplaceProductId}/publishMarketplaceProduct

Webhook delivery

  • Retries come after 1 min, 5 min, 30 min, 2 h, 6 h, 12 h and 24 h. Each attempt waits up to 10 seconds for the answer.
  • Redeliver creates a new delivery of the same event. The original delivery does not change.
  • The same event can arrive more than once. Use the event id to skip repeats.

A publication, from the request to the webhook

The path from POST /v1/listings/{listingId}/publish to the published listing:

On the first publication, the content (the MarketplaceProduct) goes in the same Job as the offer. On later ones, only the offer and its lines change, but the marketplace_product.updated with published is sent just the same.

If the marketplace refuses, the Job ends failed and the listing becomes failed. You get listing.updated (publication_failed) and job.failed, and the error is on the Job item (GET /v1/jobs/{id}/items).

An inventory movement with automatic sending

The path from a recorded sale to the new quantity on the marketplace:

With no automatic sending and no publishTargets, the movement only changes the balance: publicationJobId comes back null, and nothing goes to the marketplace. See From the balance to the marketplace.

The inventory.movement_created webhook is sent once per ledger row, with the warehouseId and the Variant balance after the command. A transfer between warehouses writes two rows and sends two events, with the same transferId. Because the Variant total does not change, it does not trigger automatic sending.