Channels

Channels on revenexx — channel types a merchant can add, the channel bootstrap a storefront calls, and the visibility gate that decides what an unassigned row means.

A channel is a route to market: a webshop, a punch-out catalogue, a feed, a kiosk. It is the second scoping axis after markets, and what it decides is assortment visibility.

Base path: https://api.revenexx.com/v1/channels.

Channel types

A type is what lets a merchant name a kind of channel this app never thought of, without waiting for a release.

Request
curl -X POST https://api.revenexx.com/v1/channels/types \
  -H "X-Revenexx-Tenant: <TENANT_SLUG>" \
  -H "X-Revenexx-Api-Key: rvxk_..." \
  -H "Content-Type: application/json" \
  -d '{"code":"kiosk","title":"Kiosk","position":40}'

code and title are the only two required. The code is trimmed, lowercased and fixed from then on — it becomes exactly what channels.type stores, and there is no foreign key behind that column to carry a rename, so every channel holding the old string would be left pointing at nothing. title is the part a merchant renames later.

Two things worth knowing about the 409:

  • The uniqueness is wider than your tenant. The code is unique on the column alone, so a code held by another tenant collides too — and the read this route does before inserting cannot see it.
  • A tenant keeps at most 200 types. The 201st is a 409 with type_limit_reached.

Creating a type changes nothing about existing channels: it is a name that becomes available, not one that gets applied. It does not become the default either — pass is_default: true for that, which demotes the current holder.

A type has its own route by row id, the uuid the list returns, not the code: GET /v1/channels/types/{id} reads one type, PUT /v1/channels/types/{id} updates it, and DELETE /v1/channels/types/{id} removes it. PUT changes everything except the code, and a different code answers 400. The example below retires a type; on the default type it answers 409 default_must_stay_active. DELETE answers 409 while a channel still uses the type, for the last type left, and for the default while no other active type can take the flag (default_must_stay_active).

Request
curl -X PUT "https://api.revenexx.com/v1/channels/types/{id}" \
  -H "X-Revenexx-Tenant: <TENANT_SLUG>" \
  -H "X-Revenexx-Api-Key: rvxk_..." \
  -H "Content-Type: application/json" \
  -d '{"active":false}'

active (boolean, default true) offers the type for new channels. Set it to false to retire a type: it stays on the channels that carry it but is no longer offered for new ones. Making a retired type the default is 409 inactive_cannot_be_default.

GET /v1/channels/types filters on active (?active=true or ?active=false), and it is the only filter that list honours. Any other value is a 400 invalid_value. A body active that is not a boolean is a 400 invalid_active. The default type cannot be retired: 409 default_must_stay_active. Make another type the default first.

Channels

Request
curl -X POST https://api.revenexx.com/v1/channels \
  -H "X-Revenexx-Tenant: <TENANT_SLUG>" \
  -H "X-Revenexx-Api-Key: rvxk_..." \
  -H "Content-Type: application/json" \
  -d '{"code":"punchout","name":"Punch-out catalogue","type":"punchout","unassigned_visibility":"assigned_only"}'
FieldMeaning
codeIdentifier, and what a caller names to select the channel. A rename detaches its assignments, see Updating and deleting a channel.
name, labelsDisplay name and localized names.
typeOne of the tenant's channel type codes.
statusactive or inactive.
is_defaultThe channel a caller naming none falls back to.
unassigned_visibilityinherit (use the tenant answer), all, or assigned_only.
positionSort order, and the tie-break when more than one channel claims default.

POST /v1/channels/defaults restores the shipped defaults.

Listing channels

GET /v1/channels pages through the channels, 50 at a time by default (limit up to 200, offset, and order as column, column.asc or column.desc). Every column is an exact-match filter, and they combine. ?code= finds the channel a scope slug names, ?is_default=true finds the fallback channel without resolving a context, and ?unassigned_visibility=assigned_only finds the channels that closed their assortment. A query parameter that is not a column of channels is refused, not ignored: 400 with unknown_filter, and details.allowed lists the parameters the list takes. An order that names an unknown column is 400 too.

Request
curl "https://api.revenexx.com/v1/channels?unassigned_visibility=assigned_only" \
  -H "X-Revenexx-Tenant: <TENANT_SLUG>" \
  -H "X-Revenexx-Api-Key: rvxk_..."

Updating and deleting a channel

PUT /v1/channels/{id} takes the fields to change and keeps the rest, and an empty body is 400. {id} is the row's uuid, not the code. The answer is the channel as it now stands. Three of its fields need care:

  • code is accepted, and nothing follows it. The assignments other apps hold against the old slug stay under that slug, so a rename silently detaches every one of them. The channel types route refuses the same edit.
  • is_default: false on the only default channel leaves the tenant with no default. Every request naming no channel then resolves to nothing (no_default_channel). Promote another channel first.
  • type naming a type the tenant has retired is 409 type_inactive. A type the tenant does not keep at all is 400. Resending the retired type a channel already carries is accepted. A code used by another channel is 409 unique_violation.
Request
curl -X PUT "https://api.revenexx.com/v1/channels/{id}" \
  -H "X-Revenexx-Tenant: <TENANT_SLUG>" \
  -H "X-Revenexx-Api-Key: rvxk_..." \
  -H "Content-Type: application/json" \
  -d '{"unassigned_visibility":"assigned_only"}'

DELETE /v1/channels/{id} removes the row and nothing else. Nothing cascades, because the app declares no foreign key. The assignments other apps hold for the channel stay where they are, keyed by the slug, and they outlive the row. A new channel created later under the same code silently adopts all of them, so choose a new code if that is not wanted. Deleting the default channel is allowed and leaves the tenant with none, as above. POST /v1/channels/defaults creates the seeded shop channel, as the default, only when no channel with the code shop exists; it never moves the flag onto an existing channel, and it does not take the flag off another one, so a tenant whose default is another channel ends up with two. To restore a default otherwise, PUT a channel with {"is_default": true}. A non-uuid {id} is 400.

Request
curl -X DELETE "https://api.revenexx.com/v1/channels/{id}" \
  -H "X-Revenexx-Tenant: <TENANT_SLUG>" \
  -H "X-Revenexx-Api-Key: rvxk_..."

The bootstrap

Request
curl "https://api.revenexx.com/v1/channels/context?channel=punchout" \
  -H "X-Revenexx-Tenant: <TENANT_SLUG>" \
  -H "X-Revenexx-Api-Key: rvxk_..."

One call tells a shop front, a punch-out front-end or a feed builder which channel it is in and what an unassigned row means there, so it applies the policy itself instead of hard-coding one.

Two things come back, not one: the channel that resolved, and the visibility policy in force for it. The policy travels with the channel because a caller holding one and not the other still cannot render anything — knowing you are in the punch-out channel says nothing about whether an unassigned product belongs in its assortment.

Resolution order

Through the public gateway it is: ?channel=, then the principal's channel claim, then the channel flagged is_default. source in the response names which of them answered.

This app also recognises a channel header and a body field, but through api.revenexx.com the header step is inert because the gateway does not forward it. Use the query parameter.

It never errors on an unknown channel

That is deliberate, and it is why this operation declares no 4xx of its own. An unknown or inactive channel answers 200 with resolved: false and a reason, so a caller can tell no such channel from the service is down:

text
channel_required    no_default_channel    unknown_channel    channel_inactive

A tenant with no channels at all answers 200 with reason: "no_default_channel". default_ambiguous says when more than one channel claims the flag — the lowest position wins.

The visibility gate

Request
curl -X POST https://api.revenexx.com/v1/channels/visibility \
  -H "X-Revenexx-Tenant: <TENANT_SLUG>" \
  -H "X-Revenexx-Api-Key: rvxk_..." \
  -H "Content-Type: application/json" \
  -d '{
        "channel": "punchout",
        "items": [
          { "id": "…", "channels": ["webshop"] },
          { "id": "…", "channels": [] }
        ]
      }'

Two cases, and only one of them needs a policy:

  • A row with channel assignments is visible if and only if the active channel is among them. No configuration involved.
  • A row without assignments is the case unassigned_channel_visibility owns: all shows it, assigned_only hides it.

A channel may override the tenant answer for itself, through its own unassigned_visibility. That is how the webshop stays open while a punch-out channel serves only its negotiated assortment.

Unassigned does not mean hidden by default. The out-of-the-box answer is open: a row nobody assigned to a channel shows everywhere. If a punch-out catalogue must be an allow-list, set assigned_only on that channel — do not rely on the assignment absence.

The same asymmetry applies to markets: a row created without X-Revenexx-Market is unassigned, and unassigned is visible everywhere. See Settings.

Vocabularies

These are the closed value sets this app owns, ready for a select box. GET /v1/channels/vocabularies lists the names with their title and description, without the values. GET /v1/channels/vocabularies/{name} returns one vocabulary with its values in the order a select box should offer them. Each value carries its title, description and a badge tone, and default_tone is what a value without one falls back to. The names are statuses, types and unassigned-visibility; any other name is 404.

Request
curl "https://api.revenexx.com/v1/channels/vocabularies/unassigned-visibility" \
  -H "X-Revenexx-Tenant: <TENANT_SLUG>" \
  -H "X-Revenexx-Api-Key: rvxk_..."

statuses and unassigned-visibility are fixed by the app. types is the tenant's own list of channel types, so it changes when a type is added, renamed or retired. A retired type is not offered, but it is still returned under inactive, so a screen can render a channel that already carries it.

Where channels show up elsewhere

PlaceEffect
A product attribute marked scopableOne value per channel, in attribute_values.channel_specific — see Products.
family_attributes.required_channelsNarrows a required attribute to named channels.
A channel-scoped price listSpecificity level 2 in the price resolution order.
carts.channel_id, orders.channel_idWhich channel the buying happened in.

Where to go next

Was this page helpful?