DealsOfferUpdatedPayload deals.offer.updated@1

Generated at from openvibe-contracts v0.34.2 and openvibe-sdk v0.5.0.

Version
1.0.0
Owner
deals
Visibility
first-party
Status
active
Compatibility
backward
Schema
https://openvibe.network/contracts/events/payloads/deals.offer.updated.v1.json

deals.offer.updated v1 (OpenVibe.Deals server/domain/indexing.js emitOffer, called from server/domain/offers.js and server/domain/importer.js). Something about a canonical offer changed; `changed` says what, and a few call sites add one reference: an edit (offers.js update: the edited columns, among title, description, category, expires_at, product_id, ai_summary, review_state, text_origin); a price/availability report by a person (observe) or a newer or re-fetched Sources item (importer importItem) ([observation] + observation_id); a second Sources item with the same link attached to an existing offer ([sources] + observation_id); a Sources item removed at the source ([sources] + source_removed; an imported offer left with no live source is disabled in the same transaction, so status is then disabled); moderators disabling it or re-activating a disabled or expired offer ([status] + moderation); a person reviewing imported or AI text ([review_state]); a merge ([merged] + merged_offer_id, sent for the merge target); an unmerge (two events: [unmerged] + unmerged_offer_id for the former target, and [unmerged] + unmerged_from for the restored offer). Written in the transaction that made the change, next to the offer's (and its product's) deals.index_document.* event. The rest of the payload is the offer as it now stands, as in deals.offer.created. Deliberately left out: old values, the description text, identities and vote counts (deals.vote.changed carries those counts). Envelope: subject { type: offer, id: <offer_id> }, visibility public when the offer is active and the SEO gate lets it be listed, otherwise internal, priority important (default), actor user:<the person who acted, usr_…>, service:<calling service> when a service acts without a person, service:deals for the importer. No consumer is built yet.

Fields

FieldTypeRequiredDescriptionConstraints
offer_idstringyesThe canonical (root) offer id.
  • pattern ^dof_[0-9A-HJKMNP-TV-Z]{26}$
slugstringyesPage slug: the slugified title plus the last six characters of the id.
  • pattern ^[a-z0-9]+(-[a-z0-9]+)*$
  • maxLength 80
urlstringyesCanonical offer page URL on Deals (/d/<slug>).
  • pattern ^https?://
  • format uri
  • maxLength 2048
titlestringyes
  • minLength 1
  • maxLength 200
statusenumyes
  • one of "active", "expired", "disabled"
storestring | nullyesStore domain: the offer link's host without a leading www.; null when none could be derived.
  • minLength 1
product_idstring | nullyesDeals product the offer is attached to, if any.
  • pattern ^dpr_[0-9A-HJKMNP-TV-Z]{26}$
latest_observationobject | nullyesThe newest observation across the offer's merge group; null only when the group has none.
freshnessenumyesfresh while the latest observation is younger than DEALS_FRESHNESS_HOURS, stale after, unobserved without one.
  • one of "fresh", "stale", "unobserved"
indexabilityobjectyesThe SEO gate decision in Search's shape (index-hooks searchIndexability).
  • no other fields
indexability.decisionenumyes
  • one of "index", "noindex"
indexability.reasonsarray of stringyesSearch reason names, e.g. stale_price, expired, takedown, owner_decision (imported or AI text awaiting review).
  • maxItems 20
  • unique items
  • items: pattern ^[a-z][a-z0-9_]{1,63}$
changedarray of enumyesWhat changed: edited columns (update), or one of observation, sources, status, review_state, merged, unmerged.
  • minItems 1
  • unique items
  • items: one of "title", "description", "category", "expires_at", "product_id", "ai_summary", "review_state", "text_origin", "observation", "sources", "status", "merged", "unmerged"
observation_idstringchanged [observation] or [sources] from a report or an import: the observation just recorded.
  • pattern ^dpo_[0-9A-HJKMNP-TV-Z]{26}$
source_removedstringchanged [sources] after a removal: the OpenVibe.Sources item that was removed (takedown, licence).
  • pattern ^itm_[0-9A-HJKMNP-TV-Z]{26}$
moderationenumchanged [status] by a moderator: disabled, or enabled (a disabled or expired offer made active again).
  • one of "disabled", "enabled"
merged_offer_idstringchanged [merged]: the duplicate now merged into this offer.
  • pattern ^dof_[0-9A-HJKMNP-TV-Z]{26}$
unmerged_offer_idstringchanged [unmerged], on the former target: the offer split back out.
  • pattern ^dof_[0-9A-HJKMNP-TV-Z]{26}$
unmerged_fromstringchanged [unmerged], on the restored offer: the offer it had been merged into.
  • pattern ^dof_[0-9A-HJKMNP-TV-Z]{26}$

Examples

From the contract's own test fixtures: valid ones validate, rejected ones must fail.

Valid: edit
{
  "offer_id": "dof_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "slug": "usb-c-charger-65w-k0mnpq",
  "url": "https://openvibe.deals/d/usb-c-charger-65w-k0mnpq",
  "title": "USB-C charger 65W (GaN)",
  "status": "active",
  "store": "shop.example",
  "product_id": null,
  "latest_observation": {
    "price": "29.99",
    "currency": "USD",
    "availability": "in_stock",
    "observed_at": "2026-09-23T10:15:00.000Z"
  },
  "freshness": "fresh",
  "indexability": {
    "decision": "index",
    "reasons": []
  },
  "changed": [
    "title",
    "category"
  ]
}
Valid: merge
{
  "offer_id": "dof_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "slug": "usb-c-charger-65w-k0mnpq",
  "url": "https://openvibe.deals/d/usb-c-charger-65w-k0mnpq",
  "title": "USB-C charger 65W",
  "status": "active",
  "store": "shop.example",
  "product_id": null,
  "latest_observation": {
    "price": "29.99",
    "currency": "USD",
    "availability": "in_stock",
    "observed_at": "2026-09-23T10:15:00.000Z"
  },
  "freshness": "fresh",
  "indexability": {
    "decision": "index",
    "reasons": []
  },
  "changed": [
    "merged"
  ],
  "merged_offer_id": "dof_01JAB2C3D4E5F6G7H8J9K0MNPV"
}
Valid: observation
{
  "offer_id": "dof_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "slug": "usb-c-charger-65w-k0mnpq",
  "url": "https://openvibe.deals/d/usb-c-charger-65w-k0mnpq",
  "title": "USB-C charger 65W",
  "status": "active",
  "store": "shop.example",
  "product_id": null,
  "latest_observation": {
    "price": "24.99",
    "currency": "USD",
    "availability": "limited",
    "observed_at": "2026-09-23T14:02:11.000Z"
  },
  "freshness": "fresh",
  "indexability": {
    "decision": "index",
    "reasons": []
  },
  "changed": [
    "observation"
  ],
  "observation_id": "dpo_01JAB2C3D4E5F6G7H8J9K0MNPT"
}
Valid: source-removed-disabled
{
  "offer_id": "dof_01JAB2C3D4E5F6G7H8J9K0MNPR",
  "slug": "garden-hose-reel-weekend-sale-k0mnpr",
  "url": "https://openvibe.deals/d/garden-hose-reel-weekend-sale-k0mnpr",
  "title": "Garden hose reel weekend sale",
  "status": "disabled",
  "store": "garden.example",
  "product_id": "dpr_01JAB2C3D4E5F6G7H8J9K0MNPS",
  "latest_observation": {
    "price": null,
    "currency": null,
    "availability": null,
    "observed_at": "2026-09-23T06:00:00.000Z"
  },
  "freshness": "fresh",
  "indexability": {
    "decision": "noindex",
    "reasons": [
      "takedown",
      "owner_decision"
    ]
  },
  "changed": [
    "sources"
  ],
  "source_removed": "itm_01JAB2C3D4E5F6G7H8J9K0MNPW"
}
Valid: unmerge-restored
{
  "offer_id": "dof_01JAB2C3D4E5F6G7H8J9K0MNPV",
  "slug": "usb-c-charger-65w-k0mnpv",
  "url": "https://openvibe.deals/d/usb-c-charger-65w-k0mnpv",
  "title": "USB-C charger 65W",
  "status": "active",
  "store": "shop.example",
  "product_id": null,
  "latest_observation": {
    "price": "29.99",
    "currency": "USD",
    "availability": "in_stock",
    "observed_at": "2026-09-23T10:15:00.000Z"
  },
  "freshness": "fresh",
  "indexability": {
    "decision": "index",
    "reasons": []
  },
  "changed": [
    "unmerged"
  ],
  "unmerged_from": "dof_01JAB2C3D4E5F6G7H8J9K0MNPQ"
}
Rejected: bad-merged-id
{
  "offer_id": "dof_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "slug": "usb-c-charger-65w-k0mnpq",
  "url": "https://openvibe.deals/d/usb-c-charger-65w-k0mnpq",
  "title": "USB-C charger 65W",
  "status": "active",
  "store": "shop.example",
  "product_id": null,
  "latest_observation": {
    "price": "29.99",
    "currency": "USD",
    "availability": "in_stock",
    "observed_at": "2026-09-23T10:15:00.000Z"
  },
  "freshness": "fresh",
  "indexability": {
    "decision": "index",
    "reasons": []
  },
  "changed": [
    "merged"
  ],
  "merged_offer_id": "B"
}
Rejected: missing-changed
{
  "offer_id": "dof_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "slug": "usb-c-charger-65w-k0mnpq",
  "url": "https://openvibe.deals/d/usb-c-charger-65w-k0mnpq",
  "title": "USB-C charger 65W",
  "status": "active",
  "store": "shop.example",
  "product_id": null,
  "latest_observation": {
    "price": "29.99",
    "currency": "USD",
    "availability": "in_stock",
    "observed_at": "2026-09-23T10:15:00.000Z"
  },
  "freshness": "fresh",
  "indexability": {
    "decision": "index",
    "reasons": []
  }
}
Rejected: unknown-change
{
  "offer_id": "dof_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "slug": "usb-c-charger-65w-k0mnpq",
  "url": "https://openvibe.deals/d/usb-c-charger-65w-k0mnpq",
  "title": "USB-C charger 65W",
  "status": "active",
  "store": "shop.example",
  "product_id": null,
  "latest_observation": {
    "price": "29.99",
    "currency": "USD",
    "availability": "in_stock",
    "observed_at": "2026-09-23T10:15:00.000Z"
  },
  "freshness": "fresh",
  "indexability": {
    "decision": "index",
    "reasons": []
  },
  "changed": [
    "price"
  ]
}

Validate

const contracts = require('openvibe-contracts');
contracts.validate('deals.offer.updated@1', value);   // { valid, errors: [{ path, message }] }