Product Storefronts v4
Sub-resource of products recording which shops a product is offered in, and how it presents itself in each of them. A realm that sells directly may run a single shop or several — a general one alongside specialised ones for particular subject areas or product forms — and this resource is the per-shop on/off switch, together with the reading sample and the search-engine metadata that go with it.
Each record is the link between one product and one shop, so a product carries at most
one record per shop. The shop itself is a separate top-level resource,
/resource/v4/storefronts, and is embedded in every record here as storefront.
Endpoints
GET /resource/v4/products/{product_id}/storefronts
Retrieve the storefronts of a product
Description
The collection returns one entry per storefront of the realm, not one entry per
shop the title is in. A shop the product was never added to still appears, as a
placeholder: _id is the empty string, enabled is false, preview_percentage is
0 and the SEO fields are null. The number of entries is therefore the number of
shops the realm runs, and enabled is the field that says whether the title is
offered in each.
The envelope is a bare {"items": [...]} — no _links, no _pagination.
This resource supports no filtering, sorting or pagination, so none of the syntax
in Resource Queries applies to it. Every query parameter is
ignored, silently and without error: ?enabled=true, ?display=2, ?page=2, a
sort parameter and an outright nonsense parameter all return the identical full list
with 200. The list is bounded by the handful of shops a realm runs, so filtering
belongs on the client. The order of items is not part of the contract.
In the example below the title is offered in the publisher's main shop but not in its
specialist one. Two things in the second entry are easy to misread: its html_*
fields are null because the entry is disabled rather than because the product has no
metadata — the first entry shows that it does — and its url is the first shop's
URL, because that field falls back to whatever shop the product is in rather than
reporting null.
Input parameters
Path parameters
| Parameter | Type | Default | Nullable | Description |
|---|---|---|---|---|
product_id |
string | No | UUID (or legacy integer id) of the product. |
Responses
{
"items": [
{
"_id": "b62d8555-5fa4-11ef-a221-00505699575c",
"enabled": true,
"free_of_charge": false,
"html_meta_description": "Der unsignierte Teppich - Roman - Marit Sallenberg - E-Book 14,99 EUR - Zeilenquarz Verlag",
"html_meta_keywords": "Roman,Gegenwartsliteratur,Marit Sallenberg,Zeilenquarz Verlag",
"html_title": null,
"preview_percentage": 25,
"storefront": {
"_id": "9ee61a17-e617-11ee-a221-00505699575c",
"_links": {
"self": "https://api.openpublishing.com/resource/v4/storefronts/9ee61a17-e617-11ee-a221-00505699575c"
},
"name": "zeilenquarz"
},
"url": "https://www.example.com/document/14937"
},
{
"_id": "",
"enabled": false,
"free_of_charge": false,
"html_meta_description": null,
"html_meta_keywords": null,
"html_title": null,
"preview_percentage": 0,
"storefront": {
"_id": "9ee62740-e617-11ee-a221-00505699575c",
"_links": {
"self": "https://api.openpublishing.com/resource/v4/storefronts/9ee62740-e617-11ee-a221-00505699575c"
},
"name": "zeilenquarz-fachbuch"
},
"url": "https://www.example.com/document/14937"
}
]
}
Schema of the response body
{
"type": "object",
"additionalProperties": false,
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"description": "One product's presence in one storefront — the link between a product and a shop.",
"example": {
"_id": "b62d8555-5fa4-11ef-a221-00505699575c",
"storefront": {
"_id": "9ee61a17-e617-11ee-a221-00505699575c",
"_links": {
"self": "https://api.openpublishing.com/resource/v4/storefronts/9ee61a17-e617-11ee-a221-00505699575c"
},
"name": "zeilenquarz"
},
"url": "https://www.example.com/document/14937",
"enabled": true,
"preview_percentage": 25,
"html_title": null,
"html_meta_description": "Der unsignierte Teppich - Roman - Marit Sallenberg - E-Book 14,99 EUR - Zeilenquarz Verlag",
"html_meta_keywords": "Roman,Gegenwartsliteratur,Marit Sallenberg,Zeilenquarz Verlag",
"free_of_charge": false
},
"properties": {
"_id": {
"type": "string",
"description": "UUID of this product-to-storefront link. It identifies the link, not the shop — the\nshop's own id is `storefront._id`, and passing that one to the single-record endpoint\nis a `404`.\n\n**The empty string** marks a placeholder — one of the entries the collection returns\nfor a shop the product was never added to. `\"\"` means \"not in this shop\", and such an\nentry is not addressable: appending the empty string to the single-record path is a\n`404`, so the record has to be created with `POST` first.\n\nThe single-record path accepts this UUID and nothing else; a legacy integer id does not\nresolve there. Storefront links carry no `_legacy_id` in any case: this resource\nidentified itself by UUID already in v3, so v3 and v4 return the same `_id`. Records\nhere also carry neither a `_version` nor a record-level `_links`, both of which most v4\nresources have.\n",
"example": "b62d8555-5fa4-11ef-a221-00505699575c"
},
"storefront": {
"type": "object",
"additionalProperties": false,
"description": "The shop this record is about — the same representation the top-level\n`/resource/v4/storefronts/{id}` endpoint returns. Realms typically run several:\na main shop plus specialised ones for particular subject areas or product forms.\n\n`{\"storefront\": {\"_id\": \"<uuid>\"}}` is what says which shop a record is for: on `POST`\nit attaches the new record to a shop, and on `PUT` it moves an existing record to\nanother shop of the same realm. Only `_id` is read; a `name` sent alongside it is\nignored. The shop must belong to the same realm as the product, and one that does not —\nlike one that does not exist at all — is rejected with `400 Bad Request`.\n\n`storefront` and `enabled` are the only two fields `PUT` and `POST` read from the body.\nEverything else sent is discarded without comment.\n",
"properties": {
"_id": {
"type": "string",
"description": "UUID of the storefront. This is the id to use against `/resource/v4/storefronts/{id}`, and the one to send when creating a record.",
"example": "9ee61a17-e617-11ee-a221-00505699575c"
},
"_links": {
"type": "object",
"description": "`self` — the storefront's own resource, `/resource/v4/storefronts/{storefront_id}`."
},
"name": {
"type": "string",
"description": "The shop's short name: a lowercase slug, unique within the realm, used as the\nstorefront's handle throughout the platform rather than as a display title. It\nis normally the first component of the shop's hostname, which is why the `url`\nof an entry usually echoes it.\n",
"example": "zeilenquarz"
}
}
},
"url": {
"type": "string",
"nullable": true,
"description": "The product's page in this shop, as `https://<shop hostname>/document/<legacy product id>`.\nNote the path uses the product's **legacy integer id**, not its UUID.\n\n`null` when there is no public page to link to, which is the case whenever any of these\nis true: the realm does not run storefronts at all, the product is not in *any*\nstorefront, or the product's `status` is not `published`. `null` is therefore expected\nfor unpublished titles, for titles in no shop, and for every title in a realm that does\nnot run shops.\n\n**It is not scoped strictly to this record.** If the product is not in *this* shop but\nis in another, the field falls back to the other shop's URL — the realm's main\nstorefront if the product is in it, otherwise the first one it is in. A disabled entry\ncan therefore still carry a working URL pointing somewhere else, and a URL is only\nreliably this record's shop when `storefront.name` matches its hostname.\n\nThe hostname is resolved for the environment answering the request, so a call against\na test system returns that system's host rather than the production one.\n",
"example": "https://www.example.com/document/14937"
},
"enabled": {
"type": "boolean",
"description": "Whether the product is offered in this storefront. This is the switch the whole record\nturns on: when it is `false` the shop does not list or sell the title, and the response\nreports `preview_percentage` as `0`, `free_of_charge` as `false` and the three `html_*`\nfields as `null`, whatever values the product itself carries.\n\nIt is `false` on placeholder entries for shops the product was never added to as well,\nso `enabled: false` alone does not distinguish \"withdrawn from this shop\" from \"never\nadded to it\" — `_id` is what distinguishes those.\n\nBecause the collection returns one entry per shop in the realm rather than one per shop\nthe title is in, the length of `items` is the number of shops the realm runs. Counting\nentries with `enabled: true` gives the number the title is in, and `enabled` is the\nfield to test for \"offered here\".\n\nThis is the one field a client can meaningfully change. A `PUT` of\n`{\"enabled\": false}` withdraws a title from one shop while leaving it in the others.\nIt does not delete the record, and there is no `DELETE` on this resource — the record\nsurvives, so switching the title back on later is another `PUT` rather than a fresh\n`POST`, and `_id` does not change.\n",
"example": true
},
"preview_percentage": {
"type": "integer",
"description": "How much of the book a visitor may read for free in this shop, as a whole-number\npercentage of its pages, `0` to `100`. This drives the reading sample the shop\nrenders — `25` means the first quarter of the text is open, the rest is behind the\npurchase.\n\nThe value is **per product, not per shop**: the same value is returned for every shop\nof a product and cannot differ between them, so every enabled entry in the list shows\nthe same number, and only the `0` reported on disabled entries differs. `0` on an\nenabled entry means no free reading sample at all.\n\n`100` is the special case: the entire text is readable without paying, which is what\nmakes `free_of_charge` `true`.\n\nRead-only here — this endpoint's `PUT` does not accept it.\n",
"example": 25
},
"html_title": {
"type": "string",
"nullable": true,
"description": "Override for the `<title>` element of the shop's product page. `null` means no override\nhas been set and the shop composes the title itself from the product metadata.\n\nThe same value is returned for every shop of a product and cannot differ between them;\ndisabled entries report `null`. Read-only through this endpoint.\n",
"example": null
},
"html_meta_description": {
"type": "string",
"nullable": true,
"description": "The `<meta name=\"description\">` of the shop's product page — the snippet a search\nengine shows under the link. Plain text, no markup, and in practice kept near the\n155 characters a search engine displays — the same length the default derived from the\nproduct's abstract is truncated to when no value has been set.\n\n`null` means no value has been set and the shop falls back to its own derivation.\n\nLike `html_title` and `html_meta_keywords`, this value is **shared, not per-shop**: the\nsame values are returned for every shop of a product and cannot differ between them, so\nevery enabled entry in the collection repeats the same three strings and one shop cannot\nbe given a different meta description from another. Disabled entries report `null` for\nall three. All three are also **read-only through this endpoint**: `PUT` and `POST` read\nonly `storefront` and `enabled`, and silently discard everything else.\n",
"example": "Der unsignierte Teppich - Roman - Marit Sallenberg - E-Book 14,99 EUR - Zeilenquarz Verlag"
},
"html_meta_keywords": {
"type": "string",
"nullable": true,
"description": "The `<meta name=\"keywords\">` of the shop's product page: a single string of\ncomma-separated terms, **not** a JSON array. The convention is a bare `,` with no space\nafter it, matching the default derived from the product's search tags when no value has\nbeen set.\n\nModern search engines ignore this element; it survives for shop-internal search and\nfor feeds that still expect it. `null` means no value has been set. The same value is\nreturned for every shop of a product and cannot differ between them; disabled entries\nreport `null`. Read-only through this endpoint.\n",
"example": "Roman,Gegenwartsliteratur,Marit Sallenberg,Zeilenquarz Verlag"
},
"free_of_charge": {
"type": "boolean",
"description": "Whether a visitor can get the whole work in this shop without paying. Derived rather\nthan set, and computed as follows:\n\n- `true` when `preview_percentage` is `100` — the complete text is in the free reading\n sample, so there is nothing left to buy. This is the intended meaning.\n- `true` otherwise when the product has **no gross e-book price** in EUR for Germany on\n today's date. A title with no priced e-book edition therefore reports\n `free_of_charge: true` even though it is not on offer at all, and a title priced only\n outside the euro area does the same.\n- `false` whenever `enabled` is `false`, without either rule being evaluated.\n\nThe field therefore means \"this shop cannot charge for the e-book\" rather than \"this is\na giveaway\", and the product's prices are the authority on which of the two situations\na `true` represents.\n",
"example": false
}
},
"required": []
}
}
},
"required": [
"items"
]
}
{
"errors": [
{
"_id": "string",
"stack_trace": [
"string"
],
"message": "string",
"vars": {}
}
]
}
Schema of the response body
{
"type": "object",
"additionalProperties": false,
"required": [
"errors"
],
"properties": {
"errors": {
"type": "array",
"description": "List of errors occurred",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"_id",
"message",
"stack_trace",
"vars"
],
"properties": {
"_id": {
"type": "string",
"description": "The identifier of the error"
},
"stack_trace": {
"type": "array",
"description": "A stacktrace of the error occurred. Only for testing systems.",
"items": {
"type": "string"
}
},
"message": {
"type": "string",
"description": "A translated message of the error"
},
"vars": {
"type": "object",
"description": "A dict containing additional values of the context of the error."
}
}
}
}
}
}
POST /resource/v4/products/{product_id}/storefronts
Add a product to a storefront
Description
Creates the product-to-storefront record that the collection endpoint shows as a placeholder, and returns it in full.
The body needs storefront._id — the UUID of a shop in the same realm as the
product — and enabled. Those are the only two fields read; everything else in the
body is ignored. Nothing else about the body is validated, and omitting storefront
is not an error: a POST without it creates a record with no shop attached — one
that will never appear in the collection, because the collection is driven by the
realm's shops rather than by the records that exist. storefront._id belongs on
every POST.
The reply is 201 with the complete record, including the derived url,
preview_percentage, free_of_charge and the product-level html_* values.
Input parameters
Path parameters
| Parameter | Type | Default | Nullable | Description |
|---|---|---|---|---|
product_id |
string | No | UUID (or legacy integer id) of the product. |
Request body
{
"storefront": {
"_id": "9ee62740-e617-11ee-a221-00505699575c"
},
"enabled": true
}
Schema of the request body
{
"type": "object",
"additionalProperties": true,
"properties": {
"storefront": {
"type": "object",
"additionalProperties": true,
"description": "Which shop to add the product to. Only `_id` is read; a `name` sent alongside it is ignored. The shop must belong to the same realm as the product.",
"properties": {
"_id": {
"type": "string",
"description": "UUID of the storefront, as returned by `/resource/v4/storefronts`.",
"example": "9ee62740-e617-11ee-a221-00505699575c"
}
}
},
"enabled": {
"type": "boolean",
"description": "Whether the product is offered in this shop. Defaults to enabled when omitted.",
"example": true
}
}
}
Responses
{
"_id": "d1a7c4b0-6011-11ef-a221-00505699575c",
"enabled": true,
"free_of_charge": false,
"html_meta_description": "Der unsignierte Teppich - Roman - Marit Sallenberg - E-Book 14,99 EUR - Zeilenquarz Verlag",
"html_meta_keywords": "Roman,Gegenwartsliteratur,Marit Sallenberg,Zeilenquarz Verlag",
"html_title": null,
"preview_percentage": 25,
"storefront": {
"_id": "9ee62740-e617-11ee-a221-00505699575c",
"_links": {
"self": "https://api.openpublishing.com/resource/v4/storefronts/9ee62740-e617-11ee-a221-00505699575c"
},
"name": "zeilenquarz-fachbuch"
},
"url": "https://www.example.com/document/14937"
}
Schema of the response body
{
"type": "object",
"additionalProperties": false,
"description": "One product's presence in one storefront — the link between a product and a shop.",
"example": {
"_id": "b62d8555-5fa4-11ef-a221-00505699575c",
"storefront": {
"_id": "9ee61a17-e617-11ee-a221-00505699575c",
"_links": {
"self": "https://api.openpublishing.com/resource/v4/storefronts/9ee61a17-e617-11ee-a221-00505699575c"
},
"name": "zeilenquarz"
},
"url": "https://www.example.com/document/14937",
"enabled": true,
"preview_percentage": 25,
"html_title": null,
"html_meta_description": "Der unsignierte Teppich - Roman - Marit Sallenberg - E-Book 14,99 EUR - Zeilenquarz Verlag",
"html_meta_keywords": "Roman,Gegenwartsliteratur,Marit Sallenberg,Zeilenquarz Verlag",
"free_of_charge": false
},
"properties": {
"_id": {
"type": "string",
"description": "UUID of this product-to-storefront link. It identifies the link, not the shop — the\nshop's own id is `storefront._id`, and passing that one to the single-record endpoint\nis a `404`.\n\n**The empty string** marks a placeholder — one of the entries the collection returns\nfor a shop the product was never added to. `\"\"` means \"not in this shop\", and such an\nentry is not addressable: appending the empty string to the single-record path is a\n`404`, so the record has to be created with `POST` first.\n\nThe single-record path accepts this UUID and nothing else; a legacy integer id does not\nresolve there. Storefront links carry no `_legacy_id` in any case: this resource\nidentified itself by UUID already in v3, so v3 and v4 return the same `_id`. Records\nhere also carry neither a `_version` nor a record-level `_links`, both of which most v4\nresources have.\n",
"example": "b62d8555-5fa4-11ef-a221-00505699575c"
},
"storefront": {
"type": "object",
"additionalProperties": false,
"description": "The shop this record is about — the same representation the top-level\n`/resource/v4/storefronts/{id}` endpoint returns. Realms typically run several:\na main shop plus specialised ones for particular subject areas or product forms.\n\n`{\"storefront\": {\"_id\": \"<uuid>\"}}` is what says which shop a record is for: on `POST`\nit attaches the new record to a shop, and on `PUT` it moves an existing record to\nanother shop of the same realm. Only `_id` is read; a `name` sent alongside it is\nignored. The shop must belong to the same realm as the product, and one that does not —\nlike one that does not exist at all — is rejected with `400 Bad Request`.\n\n`storefront` and `enabled` are the only two fields `PUT` and `POST` read from the body.\nEverything else sent is discarded without comment.\n",
"properties": {
"_id": {
"type": "string",
"description": "UUID of the storefront. This is the id to use against `/resource/v4/storefronts/{id}`, and the one to send when creating a record.",
"example": "9ee61a17-e617-11ee-a221-00505699575c"
},
"_links": {
"type": "object",
"description": "`self` — the storefront's own resource, `/resource/v4/storefronts/{storefront_id}`."
},
"name": {
"type": "string",
"description": "The shop's short name: a lowercase slug, unique within the realm, used as the\nstorefront's handle throughout the platform rather than as a display title. It\nis normally the first component of the shop's hostname, which is why the `url`\nof an entry usually echoes it.\n",
"example": "zeilenquarz"
}
}
},
"url": {
"type": "string",
"nullable": true,
"description": "The product's page in this shop, as `https://<shop hostname>/document/<legacy product id>`.\nNote the path uses the product's **legacy integer id**, not its UUID.\n\n`null` when there is no public page to link to, which is the case whenever any of these\nis true: the realm does not run storefronts at all, the product is not in *any*\nstorefront, or the product's `status` is not `published`. `null` is therefore expected\nfor unpublished titles, for titles in no shop, and for every title in a realm that does\nnot run shops.\n\n**It is not scoped strictly to this record.** If the product is not in *this* shop but\nis in another, the field falls back to the other shop's URL — the realm's main\nstorefront if the product is in it, otherwise the first one it is in. A disabled entry\ncan therefore still carry a working URL pointing somewhere else, and a URL is only\nreliably this record's shop when `storefront.name` matches its hostname.\n\nThe hostname is resolved for the environment answering the request, so a call against\na test system returns that system's host rather than the production one.\n",
"example": "https://www.example.com/document/14937"
},
"enabled": {
"type": "boolean",
"description": "Whether the product is offered in this storefront. This is the switch the whole record\nturns on: when it is `false` the shop does not list or sell the title, and the response\nreports `preview_percentage` as `0`, `free_of_charge` as `false` and the three `html_*`\nfields as `null`, whatever values the product itself carries.\n\nIt is `false` on placeholder entries for shops the product was never added to as well,\nso `enabled: false` alone does not distinguish \"withdrawn from this shop\" from \"never\nadded to it\" — `_id` is what distinguishes those.\n\nBecause the collection returns one entry per shop in the realm rather than one per shop\nthe title is in, the length of `items` is the number of shops the realm runs. Counting\nentries with `enabled: true` gives the number the title is in, and `enabled` is the\nfield to test for \"offered here\".\n\nThis is the one field a client can meaningfully change. A `PUT` of\n`{\"enabled\": false}` withdraws a title from one shop while leaving it in the others.\nIt does not delete the record, and there is no `DELETE` on this resource — the record\nsurvives, so switching the title back on later is another `PUT` rather than a fresh\n`POST`, and `_id` does not change.\n",
"example": true
},
"preview_percentage": {
"type": "integer",
"description": "How much of the book a visitor may read for free in this shop, as a whole-number\npercentage of its pages, `0` to `100`. This drives the reading sample the shop\nrenders — `25` means the first quarter of the text is open, the rest is behind the\npurchase.\n\nThe value is **per product, not per shop**: the same value is returned for every shop\nof a product and cannot differ between them, so every enabled entry in the list shows\nthe same number, and only the `0` reported on disabled entries differs. `0` on an\nenabled entry means no free reading sample at all.\n\n`100` is the special case: the entire text is readable without paying, which is what\nmakes `free_of_charge` `true`.\n\nRead-only here — this endpoint's `PUT` does not accept it.\n",
"example": 25
},
"html_title": {
"type": "string",
"nullable": true,
"description": "Override for the `<title>` element of the shop's product page. `null` means no override\nhas been set and the shop composes the title itself from the product metadata.\n\nThe same value is returned for every shop of a product and cannot differ between them;\ndisabled entries report `null`. Read-only through this endpoint.\n",
"example": null
},
"html_meta_description": {
"type": "string",
"nullable": true,
"description": "The `<meta name=\"description\">` of the shop's product page — the snippet a search\nengine shows under the link. Plain text, no markup, and in practice kept near the\n155 characters a search engine displays — the same length the default derived from the\nproduct's abstract is truncated to when no value has been set.\n\n`null` means no value has been set and the shop falls back to its own derivation.\n\nLike `html_title` and `html_meta_keywords`, this value is **shared, not per-shop**: the\nsame values are returned for every shop of a product and cannot differ between them, so\nevery enabled entry in the collection repeats the same three strings and one shop cannot\nbe given a different meta description from another. Disabled entries report `null` for\nall three. All three are also **read-only through this endpoint**: `PUT` and `POST` read\nonly `storefront` and `enabled`, and silently discard everything else.\n",
"example": "Der unsignierte Teppich - Roman - Marit Sallenberg - E-Book 14,99 EUR - Zeilenquarz Verlag"
},
"html_meta_keywords": {
"type": "string",
"nullable": true,
"description": "The `<meta name=\"keywords\">` of the shop's product page: a single string of\ncomma-separated terms, **not** a JSON array. The convention is a bare `,` with no space\nafter it, matching the default derived from the product's search tags when no value has\nbeen set.\n\nModern search engines ignore this element; it survives for shop-internal search and\nfor feeds that still expect it. `null` means no value has been set. The same value is\nreturned for every shop of a product and cannot differ between them; disabled entries\nreport `null`. Read-only through this endpoint.\n",
"example": "Roman,Gegenwartsliteratur,Marit Sallenberg,Zeilenquarz Verlag"
},
"free_of_charge": {
"type": "boolean",
"description": "Whether a visitor can get the whole work in this shop without paying. Derived rather\nthan set, and computed as follows:\n\n- `true` when `preview_percentage` is `100` — the complete text is in the free reading\n sample, so there is nothing left to buy. This is the intended meaning.\n- `true` otherwise when the product has **no gross e-book price** in EUR for Germany on\n today's date. A title with no priced e-book edition therefore reports\n `free_of_charge: true` even though it is not on offer at all, and a title priced only\n outside the euro area does the same.\n- `false` whenever `enabled` is `false`, without either rule being evaluated.\n\nThe field therefore means \"this shop cannot charge for the e-book\" rather than \"this is\na giveaway\", and the product's prices are the authority on which of the two situations\na `true` represents.\n",
"example": false
}
},
"required": []
}
{
"errors": [
{
"_id": "string",
"stack_trace": [
"string"
],
"message": "string",
"vars": {}
}
]
}
Schema of the response body
{
"type": "object",
"additionalProperties": false,
"required": [
"errors"
],
"properties": {
"errors": {
"type": "array",
"description": "List of errors occurred",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"_id",
"message",
"stack_trace",
"vars"
],
"properties": {
"_id": {
"type": "string",
"description": "The identifier of the error"
},
"stack_trace": {
"type": "array",
"description": "A stacktrace of the error occurred. Only for testing systems.",
"items": {
"type": "string"
}
},
"message": {
"type": "string",
"description": "A translated message of the error"
},
"vars": {
"type": "object",
"description": "A dict containing additional values of the context of the error."
}
}
}
}
}
}
{
"errors": [
{
"_id": "string",
"stack_trace": [
"string"
],
"message": "string",
"vars": {}
}
]
}
Schema of the response body
{
"type": "object",
"additionalProperties": false,
"required": [
"errors"
],
"properties": {
"errors": {
"type": "array",
"description": "List of errors occurred",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"_id",
"message",
"stack_trace",
"vars"
],
"properties": {
"_id": {
"type": "string",
"description": "The identifier of the error"
},
"stack_trace": {
"type": "array",
"description": "A stacktrace of the error occurred. Only for testing systems.",
"items": {
"type": "string"
}
},
"message": {
"type": "string",
"description": "A translated message of the error"
},
"vars": {
"type": "object",
"description": "A dict containing additional values of the context of the error."
}
}
}
}
}
}
GET /resource/v4/products/{product_id}/storefronts/{storefront_record_id}
Retrieve a single product storefront record
Description
Fetches one product-to-storefront record by the _id the collection endpoint
returned.
That id belongs to the link, not to the shop. Passing storefront._id — the
shop's own UUID — is a 404, and so is the empty _id of a placeholder entry,
because no record exists behind it. The UUID is the only accepted form; a legacy
integer id does not resolve here, and records on this resource carry no _legacy_id
in any case.
Input parameters
Path parameters
| Parameter | Type | Default | Nullable | Description |
|---|---|---|---|---|
product_id |
string | No | UUID (or legacy integer id) of the product. | |
storefront_record_id |
string | No | UUID of the product-to-storefront record — the `_id` of a list entry, not `storefront._id`. |
Responses
{
"_id": "b62d8555-5fa4-11ef-a221-00505699575c",
"enabled": true,
"free_of_charge": false,
"html_meta_description": "Der unsignierte Teppich - Roman - Marit Sallenberg - E-Book 14,99 EUR - Zeilenquarz Verlag",
"html_meta_keywords": "Roman,Gegenwartsliteratur,Marit Sallenberg,Zeilenquarz Verlag",
"html_title": null,
"preview_percentage": 25,
"storefront": {
"_id": "9ee61a17-e617-11ee-a221-00505699575c",
"_links": {
"self": "https://api.openpublishing.com/resource/v4/storefronts/9ee61a17-e617-11ee-a221-00505699575c"
},
"name": "zeilenquarz"
},
"url": "https://www.example.com/document/14937"
}
Schema of the response body
{
"type": "object",
"additionalProperties": false,
"description": "One product's presence in one storefront — the link between a product and a shop.",
"example": {
"_id": "b62d8555-5fa4-11ef-a221-00505699575c",
"storefront": {
"_id": "9ee61a17-e617-11ee-a221-00505699575c",
"_links": {
"self": "https://api.openpublishing.com/resource/v4/storefronts/9ee61a17-e617-11ee-a221-00505699575c"
},
"name": "zeilenquarz"
},
"url": "https://www.example.com/document/14937",
"enabled": true,
"preview_percentage": 25,
"html_title": null,
"html_meta_description": "Der unsignierte Teppich - Roman - Marit Sallenberg - E-Book 14,99 EUR - Zeilenquarz Verlag",
"html_meta_keywords": "Roman,Gegenwartsliteratur,Marit Sallenberg,Zeilenquarz Verlag",
"free_of_charge": false
},
"properties": {
"_id": {
"type": "string",
"description": "UUID of this product-to-storefront link. It identifies the link, not the shop — the\nshop's own id is `storefront._id`, and passing that one to the single-record endpoint\nis a `404`.\n\n**The empty string** marks a placeholder — one of the entries the collection returns\nfor a shop the product was never added to. `\"\"` means \"not in this shop\", and such an\nentry is not addressable: appending the empty string to the single-record path is a\n`404`, so the record has to be created with `POST` first.\n\nThe single-record path accepts this UUID and nothing else; a legacy integer id does not\nresolve there. Storefront links carry no `_legacy_id` in any case: this resource\nidentified itself by UUID already in v3, so v3 and v4 return the same `_id`. Records\nhere also carry neither a `_version` nor a record-level `_links`, both of which most v4\nresources have.\n",
"example": "b62d8555-5fa4-11ef-a221-00505699575c"
},
"storefront": {
"type": "object",
"additionalProperties": false,
"description": "The shop this record is about — the same representation the top-level\n`/resource/v4/storefronts/{id}` endpoint returns. Realms typically run several:\na main shop plus specialised ones for particular subject areas or product forms.\n\n`{\"storefront\": {\"_id\": \"<uuid>\"}}` is what says which shop a record is for: on `POST`\nit attaches the new record to a shop, and on `PUT` it moves an existing record to\nanother shop of the same realm. Only `_id` is read; a `name` sent alongside it is\nignored. The shop must belong to the same realm as the product, and one that does not —\nlike one that does not exist at all — is rejected with `400 Bad Request`.\n\n`storefront` and `enabled` are the only two fields `PUT` and `POST` read from the body.\nEverything else sent is discarded without comment.\n",
"properties": {
"_id": {
"type": "string",
"description": "UUID of the storefront. This is the id to use against `/resource/v4/storefronts/{id}`, and the one to send when creating a record.",
"example": "9ee61a17-e617-11ee-a221-00505699575c"
},
"_links": {
"type": "object",
"description": "`self` — the storefront's own resource, `/resource/v4/storefronts/{storefront_id}`."
},
"name": {
"type": "string",
"description": "The shop's short name: a lowercase slug, unique within the realm, used as the\nstorefront's handle throughout the platform rather than as a display title. It\nis normally the first component of the shop's hostname, which is why the `url`\nof an entry usually echoes it.\n",
"example": "zeilenquarz"
}
}
},
"url": {
"type": "string",
"nullable": true,
"description": "The product's page in this shop, as `https://<shop hostname>/document/<legacy product id>`.\nNote the path uses the product's **legacy integer id**, not its UUID.\n\n`null` when there is no public page to link to, which is the case whenever any of these\nis true: the realm does not run storefronts at all, the product is not in *any*\nstorefront, or the product's `status` is not `published`. `null` is therefore expected\nfor unpublished titles, for titles in no shop, and for every title in a realm that does\nnot run shops.\n\n**It is not scoped strictly to this record.** If the product is not in *this* shop but\nis in another, the field falls back to the other shop's URL — the realm's main\nstorefront if the product is in it, otherwise the first one it is in. A disabled entry\ncan therefore still carry a working URL pointing somewhere else, and a URL is only\nreliably this record's shop when `storefront.name` matches its hostname.\n\nThe hostname is resolved for the environment answering the request, so a call against\na test system returns that system's host rather than the production one.\n",
"example": "https://www.example.com/document/14937"
},
"enabled": {
"type": "boolean",
"description": "Whether the product is offered in this storefront. This is the switch the whole record\nturns on: when it is `false` the shop does not list or sell the title, and the response\nreports `preview_percentage` as `0`, `free_of_charge` as `false` and the three `html_*`\nfields as `null`, whatever values the product itself carries.\n\nIt is `false` on placeholder entries for shops the product was never added to as well,\nso `enabled: false` alone does not distinguish \"withdrawn from this shop\" from \"never\nadded to it\" — `_id` is what distinguishes those.\n\nBecause the collection returns one entry per shop in the realm rather than one per shop\nthe title is in, the length of `items` is the number of shops the realm runs. Counting\nentries with `enabled: true` gives the number the title is in, and `enabled` is the\nfield to test for \"offered here\".\n\nThis is the one field a client can meaningfully change. A `PUT` of\n`{\"enabled\": false}` withdraws a title from one shop while leaving it in the others.\nIt does not delete the record, and there is no `DELETE` on this resource — the record\nsurvives, so switching the title back on later is another `PUT` rather than a fresh\n`POST`, and `_id` does not change.\n",
"example": true
},
"preview_percentage": {
"type": "integer",
"description": "How much of the book a visitor may read for free in this shop, as a whole-number\npercentage of its pages, `0` to `100`. This drives the reading sample the shop\nrenders — `25` means the first quarter of the text is open, the rest is behind the\npurchase.\n\nThe value is **per product, not per shop**: the same value is returned for every shop\nof a product and cannot differ between them, so every enabled entry in the list shows\nthe same number, and only the `0` reported on disabled entries differs. `0` on an\nenabled entry means no free reading sample at all.\n\n`100` is the special case: the entire text is readable without paying, which is what\nmakes `free_of_charge` `true`.\n\nRead-only here — this endpoint's `PUT` does not accept it.\n",
"example": 25
},
"html_title": {
"type": "string",
"nullable": true,
"description": "Override for the `<title>` element of the shop's product page. `null` means no override\nhas been set and the shop composes the title itself from the product metadata.\n\nThe same value is returned for every shop of a product and cannot differ between them;\ndisabled entries report `null`. Read-only through this endpoint.\n",
"example": null
},
"html_meta_description": {
"type": "string",
"nullable": true,
"description": "The `<meta name=\"description\">` of the shop's product page — the snippet a search\nengine shows under the link. Plain text, no markup, and in practice kept near the\n155 characters a search engine displays — the same length the default derived from the\nproduct's abstract is truncated to when no value has been set.\n\n`null` means no value has been set and the shop falls back to its own derivation.\n\nLike `html_title` and `html_meta_keywords`, this value is **shared, not per-shop**: the\nsame values are returned for every shop of a product and cannot differ between them, so\nevery enabled entry in the collection repeats the same three strings and one shop cannot\nbe given a different meta description from another. Disabled entries report `null` for\nall three. All three are also **read-only through this endpoint**: `PUT` and `POST` read\nonly `storefront` and `enabled`, and silently discard everything else.\n",
"example": "Der unsignierte Teppich - Roman - Marit Sallenberg - E-Book 14,99 EUR - Zeilenquarz Verlag"
},
"html_meta_keywords": {
"type": "string",
"nullable": true,
"description": "The `<meta name=\"keywords\">` of the shop's product page: a single string of\ncomma-separated terms, **not** a JSON array. The convention is a bare `,` with no space\nafter it, matching the default derived from the product's search tags when no value has\nbeen set.\n\nModern search engines ignore this element; it survives for shop-internal search and\nfor feeds that still expect it. `null` means no value has been set. The same value is\nreturned for every shop of a product and cannot differ between them; disabled entries\nreport `null`. Read-only through this endpoint.\n",
"example": "Roman,Gegenwartsliteratur,Marit Sallenberg,Zeilenquarz Verlag"
},
"free_of_charge": {
"type": "boolean",
"description": "Whether a visitor can get the whole work in this shop without paying. Derived rather\nthan set, and computed as follows:\n\n- `true` when `preview_percentage` is `100` — the complete text is in the free reading\n sample, so there is nothing left to buy. This is the intended meaning.\n- `true` otherwise when the product has **no gross e-book price** in EUR for Germany on\n today's date. A title with no priced e-book edition therefore reports\n `free_of_charge: true` even though it is not on offer at all, and a title priced only\n outside the euro area does the same.\n- `false` whenever `enabled` is `false`, without either rule being evaluated.\n\nThe field therefore means \"this shop cannot charge for the e-book\" rather than \"this is\na giveaway\", and the product's prices are the authority on which of the two situations\na `true` represents.\n",
"example": false
}
},
"required": []
}
{
"errors": [
{
"_id": "string",
"stack_trace": [
"string"
],
"message": "string",
"vars": {}
}
]
}
Schema of the response body
{
"type": "object",
"additionalProperties": false,
"required": [
"errors"
],
"properties": {
"errors": {
"type": "array",
"description": "List of errors occurred",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"_id",
"message",
"stack_trace",
"vars"
],
"properties": {
"_id": {
"type": "string",
"description": "The identifier of the error"
},
"stack_trace": {
"type": "array",
"description": "A stacktrace of the error occurred. Only for testing systems.",
"items": {
"type": "string"
}
},
"message": {
"type": "string",
"description": "A translated message of the error"
},
"vars": {
"type": "object",
"description": "A dict containing additional values of the context of the error."
}
}
}
}
}
}
PUT /resource/v4/products/{product_id}/storefronts/{storefront_record_id}
Update a product storefront record
Description
A partial update: each field is applied only if the body contains it, so
{"enabled": false} withdraws the product from the shop and leaves everything else
untouched. Sending storefront._id moves the record to a different shop of the same
realm.
Only those two fields are read. preview_percentage, url, free_of_charge and
the three html_* fields are read-only on this resource and are ignored if sent.
{"enabled": false} is also the only way to take a title out of a shop: there is no
DELETE on this resource. The record survives, so switching the title back on later
is another PUT and _id does not change.
The reply is 201 Resource created, not 200, and carries the updated record. The
example withdraws the title from the shop.
Input parameters
Path parameters
| Parameter | Type | Default | Nullable | Description |
|---|---|---|---|---|
product_id |
string | No | UUID (or legacy integer id) of the product. | |
storefront_record_id |
string | No | UUID of the product-to-storefront record. |
Request body
{
"enabled": false
}
Schema of the request body
{
"type": "object",
"additionalProperties": true,
"properties": {
"storefront": {
"type": "object",
"additionalProperties": true,
"description": "Move the record to another shop of the same realm. Only `_id` is read. Omit it to leave the shop assignment unchanged.",
"properties": {
"_id": {
"type": "string",
"description": "UUID of the storefront.",
"example": "9ee62740-e617-11ee-a221-00505699575c"
}
}
},
"enabled": {
"type": "boolean",
"description": "Offer the product in this shop, or withdraw it. Omit to leave unchanged. Setting it to `false` is how a title is removed from a shop; there is no `DELETE` on this resource.",
"example": false
}
}
}
Responses
{
"_id": "b62d8555-5fa4-11ef-a221-00505699575c",
"enabled": false,
"free_of_charge": false,
"html_meta_description": null,
"html_meta_keywords": null,
"html_title": null,
"preview_percentage": 0,
"storefront": {
"_id": "9ee61a17-e617-11ee-a221-00505699575c",
"_links": {
"self": "https://api.openpublishing.com/resource/v4/storefronts/9ee61a17-e617-11ee-a221-00505699575c"
},
"name": "zeilenquarz"
},
"url": "https://www.example.com/document/14937"
}
Schema of the response body
{
"type": "object",
"additionalProperties": false,
"description": "One product's presence in one storefront — the link between a product and a shop.",
"example": {
"_id": "b62d8555-5fa4-11ef-a221-00505699575c",
"storefront": {
"_id": "9ee61a17-e617-11ee-a221-00505699575c",
"_links": {
"self": "https://api.openpublishing.com/resource/v4/storefronts/9ee61a17-e617-11ee-a221-00505699575c"
},
"name": "zeilenquarz"
},
"url": "https://www.example.com/document/14937",
"enabled": true,
"preview_percentage": 25,
"html_title": null,
"html_meta_description": "Der unsignierte Teppich - Roman - Marit Sallenberg - E-Book 14,99 EUR - Zeilenquarz Verlag",
"html_meta_keywords": "Roman,Gegenwartsliteratur,Marit Sallenberg,Zeilenquarz Verlag",
"free_of_charge": false
},
"properties": {
"_id": {
"type": "string",
"description": "UUID of this product-to-storefront link. It identifies the link, not the shop — the\nshop's own id is `storefront._id`, and passing that one to the single-record endpoint\nis a `404`.\n\n**The empty string** marks a placeholder — one of the entries the collection returns\nfor a shop the product was never added to. `\"\"` means \"not in this shop\", and such an\nentry is not addressable: appending the empty string to the single-record path is a\n`404`, so the record has to be created with `POST` first.\n\nThe single-record path accepts this UUID and nothing else; a legacy integer id does not\nresolve there. Storefront links carry no `_legacy_id` in any case: this resource\nidentified itself by UUID already in v3, so v3 and v4 return the same `_id`. Records\nhere also carry neither a `_version` nor a record-level `_links`, both of which most v4\nresources have.\n",
"example": "b62d8555-5fa4-11ef-a221-00505699575c"
},
"storefront": {
"type": "object",
"additionalProperties": false,
"description": "The shop this record is about — the same representation the top-level\n`/resource/v4/storefronts/{id}` endpoint returns. Realms typically run several:\na main shop plus specialised ones for particular subject areas or product forms.\n\n`{\"storefront\": {\"_id\": \"<uuid>\"}}` is what says which shop a record is for: on `POST`\nit attaches the new record to a shop, and on `PUT` it moves an existing record to\nanother shop of the same realm. Only `_id` is read; a `name` sent alongside it is\nignored. The shop must belong to the same realm as the product, and one that does not —\nlike one that does not exist at all — is rejected with `400 Bad Request`.\n\n`storefront` and `enabled` are the only two fields `PUT` and `POST` read from the body.\nEverything else sent is discarded without comment.\n",
"properties": {
"_id": {
"type": "string",
"description": "UUID of the storefront. This is the id to use against `/resource/v4/storefronts/{id}`, and the one to send when creating a record.",
"example": "9ee61a17-e617-11ee-a221-00505699575c"
},
"_links": {
"type": "object",
"description": "`self` — the storefront's own resource, `/resource/v4/storefronts/{storefront_id}`."
},
"name": {
"type": "string",
"description": "The shop's short name: a lowercase slug, unique within the realm, used as the\nstorefront's handle throughout the platform rather than as a display title. It\nis normally the first component of the shop's hostname, which is why the `url`\nof an entry usually echoes it.\n",
"example": "zeilenquarz"
}
}
},
"url": {
"type": "string",
"nullable": true,
"description": "The product's page in this shop, as `https://<shop hostname>/document/<legacy product id>`.\nNote the path uses the product's **legacy integer id**, not its UUID.\n\n`null` when there is no public page to link to, which is the case whenever any of these\nis true: the realm does not run storefronts at all, the product is not in *any*\nstorefront, or the product's `status` is not `published`. `null` is therefore expected\nfor unpublished titles, for titles in no shop, and for every title in a realm that does\nnot run shops.\n\n**It is not scoped strictly to this record.** If the product is not in *this* shop but\nis in another, the field falls back to the other shop's URL — the realm's main\nstorefront if the product is in it, otherwise the first one it is in. A disabled entry\ncan therefore still carry a working URL pointing somewhere else, and a URL is only\nreliably this record's shop when `storefront.name` matches its hostname.\n\nThe hostname is resolved for the environment answering the request, so a call against\na test system returns that system's host rather than the production one.\n",
"example": "https://www.example.com/document/14937"
},
"enabled": {
"type": "boolean",
"description": "Whether the product is offered in this storefront. This is the switch the whole record\nturns on: when it is `false` the shop does not list or sell the title, and the response\nreports `preview_percentage` as `0`, `free_of_charge` as `false` and the three `html_*`\nfields as `null`, whatever values the product itself carries.\n\nIt is `false` on placeholder entries for shops the product was never added to as well,\nso `enabled: false` alone does not distinguish \"withdrawn from this shop\" from \"never\nadded to it\" — `_id` is what distinguishes those.\n\nBecause the collection returns one entry per shop in the realm rather than one per shop\nthe title is in, the length of `items` is the number of shops the realm runs. Counting\nentries with `enabled: true` gives the number the title is in, and `enabled` is the\nfield to test for \"offered here\".\n\nThis is the one field a client can meaningfully change. A `PUT` of\n`{\"enabled\": false}` withdraws a title from one shop while leaving it in the others.\nIt does not delete the record, and there is no `DELETE` on this resource — the record\nsurvives, so switching the title back on later is another `PUT` rather than a fresh\n`POST`, and `_id` does not change.\n",
"example": true
},
"preview_percentage": {
"type": "integer",
"description": "How much of the book a visitor may read for free in this shop, as a whole-number\npercentage of its pages, `0` to `100`. This drives the reading sample the shop\nrenders — `25` means the first quarter of the text is open, the rest is behind the\npurchase.\n\nThe value is **per product, not per shop**: the same value is returned for every shop\nof a product and cannot differ between them, so every enabled entry in the list shows\nthe same number, and only the `0` reported on disabled entries differs. `0` on an\nenabled entry means no free reading sample at all.\n\n`100` is the special case: the entire text is readable without paying, which is what\nmakes `free_of_charge` `true`.\n\nRead-only here — this endpoint's `PUT` does not accept it.\n",
"example": 25
},
"html_title": {
"type": "string",
"nullable": true,
"description": "Override for the `<title>` element of the shop's product page. `null` means no override\nhas been set and the shop composes the title itself from the product metadata.\n\nThe same value is returned for every shop of a product and cannot differ between them;\ndisabled entries report `null`. Read-only through this endpoint.\n",
"example": null
},
"html_meta_description": {
"type": "string",
"nullable": true,
"description": "The `<meta name=\"description\">` of the shop's product page — the snippet a search\nengine shows under the link. Plain text, no markup, and in practice kept near the\n155 characters a search engine displays — the same length the default derived from the\nproduct's abstract is truncated to when no value has been set.\n\n`null` means no value has been set and the shop falls back to its own derivation.\n\nLike `html_title` and `html_meta_keywords`, this value is **shared, not per-shop**: the\nsame values are returned for every shop of a product and cannot differ between them, so\nevery enabled entry in the collection repeats the same three strings and one shop cannot\nbe given a different meta description from another. Disabled entries report `null` for\nall three. All three are also **read-only through this endpoint**: `PUT` and `POST` read\nonly `storefront` and `enabled`, and silently discard everything else.\n",
"example": "Der unsignierte Teppich - Roman - Marit Sallenberg - E-Book 14,99 EUR - Zeilenquarz Verlag"
},
"html_meta_keywords": {
"type": "string",
"nullable": true,
"description": "The `<meta name=\"keywords\">` of the shop's product page: a single string of\ncomma-separated terms, **not** a JSON array. The convention is a bare `,` with no space\nafter it, matching the default derived from the product's search tags when no value has\nbeen set.\n\nModern search engines ignore this element; it survives for shop-internal search and\nfor feeds that still expect it. `null` means no value has been set. The same value is\nreturned for every shop of a product and cannot differ between them; disabled entries\nreport `null`. Read-only through this endpoint.\n",
"example": "Roman,Gegenwartsliteratur,Marit Sallenberg,Zeilenquarz Verlag"
},
"free_of_charge": {
"type": "boolean",
"description": "Whether a visitor can get the whole work in this shop without paying. Derived rather\nthan set, and computed as follows:\n\n- `true` when `preview_percentage` is `100` — the complete text is in the free reading\n sample, so there is nothing left to buy. This is the intended meaning.\n- `true` otherwise when the product has **no gross e-book price** in EUR for Germany on\n today's date. A title with no priced e-book edition therefore reports\n `free_of_charge: true` even though it is not on offer at all, and a title priced only\n outside the euro area does the same.\n- `false` whenever `enabled` is `false`, without either rule being evaluated.\n\nThe field therefore means \"this shop cannot charge for the e-book\" rather than \"this is\na giveaway\", and the product's prices are the authority on which of the two situations\na `true` represents.\n",
"example": false
}
},
"required": []
}
{
"errors": [
{
"_id": "string",
"stack_trace": [
"string"
],
"message": "string",
"vars": {}
}
]
}
Schema of the response body
{
"type": "object",
"additionalProperties": false,
"required": [
"errors"
],
"properties": {
"errors": {
"type": "array",
"description": "List of errors occurred",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"_id",
"message",
"stack_trace",
"vars"
],
"properties": {
"_id": {
"type": "string",
"description": "The identifier of the error"
},
"stack_trace": {
"type": "array",
"description": "A stacktrace of the error occurred. Only for testing systems.",
"items": {
"type": "string"
}
},
"message": {
"type": "string",
"description": "A translated message of the error"
},
"vars": {
"type": "object",
"description": "A dict containing additional values of the context of the error."
}
}
}
}
}
}
{
"errors": [
{
"_id": "string",
"stack_trace": [
"string"
],
"message": "string",
"vars": {}
}
]
}
Schema of the response body
{
"type": "object",
"additionalProperties": false,
"required": [
"errors"
],
"properties": {
"errors": {
"type": "array",
"description": "List of errors occurred",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"_id",
"message",
"stack_trace",
"vars"
],
"properties": {
"_id": {
"type": "string",
"description": "The identifier of the error"
},
"stack_trace": {
"type": "array",
"description": "A stacktrace of the error occurred. Only for testing systems.",
"items": {
"type": "string"
}
},
"message": {
"type": "string",
"description": "A translated message of the error"
},
"vars": {
"type": "object",
"description": "A dict containing additional values of the context of the error."
}
}
}
}
}
}
Schemas
Product Storefront
| Name | Type | Description | Example |
|---|---|---|---|
_id |
string |
UUID of this product-to-storefront link. It identifies the link, not the shop — the
shop's own id is The empty string marks a placeholder — one of the entries the collection returns
for a shop the product was never added to. The single-record path accepts this UUID and nothing else; a legacy integer id does not
resolve there. Storefront links carry no |
b62d8555-5fa4-11ef-a221-00505699575c |
enabled |
boolean |
Whether the product is offered in this storefront. This is the switch the whole record
turns on: when it is It is Because the collection returns one entry per shop in the realm rather than one per shop
the title is in, the length of This is the one field a client can meaningfully change. A |
true |
free_of_charge |
boolean |
Whether a visitor can get the whole work in this shop without paying. Derived rather than set, and computed as follows:
The field therefore means "this shop cannot charge for the e-book" rather than "this is
a giveaway", and the product's prices are the authority on which of the two situations
a |
false |
html_meta_description |
string| null |
The
Like |
Der unsignierte Teppich - Roman - Marit Sallenberg - E-Book 14,99 EUR - Zeilenquarz Verlag |
html_meta_keywords |
string| null |
The Modern search engines ignore this element; it survives for shop-internal search and
for feeds that still expect it. |
Roman,Gegenwartsliteratur,Marit Sallenberg,Zeilenquarz Verlag |
html_title |
string| null |
Override for the The same value is returned for every shop of a product and cannot differ between them;
disabled entries report |
|
preview_percentage |
integer |
How much of the book a visitor may read for free in this shop, as a whole-number
percentage of its pages, The value is per product, not per shop: the same value is returned for every shop
of a product and cannot differ between them, so every enabled entry in the list shows
the same number, and only the
Read-only here — this endpoint's |
25 |
storefront |
Properties: _id, _links, name |
The shop this record is about — the same representation the top-level
|
|
storefront._id |
string |
UUID of the storefront. This is the id to use against |
9ee61a17-e617-11ee-a221-00505699575c |
storefront._links |
object |
|
|
storefront.name |
string |
The shop's short name: a lowercase slug, unique within the realm, used as the
storefront's handle throughout the platform rather than as a display title. It
is normally the first component of the shop's hostname, which is why the |
zeilenquarz |
url |
string| null |
The product's page in this shop, as
It is not scoped strictly to this record. If the product is not in this shop but
is in another, the field falls back to the other shop's URL — the realm's main
storefront if the product is in it, otherwise the first one it is in. A disabled entry
can therefore still carry a working URL pointing somewhere else, and a URL is only
reliably this record's shop when The hostname is resolved for the environment answering the request, so a call against a test system returns that system's host rather than the production one. |
https://www.example.com/document/14937 |