{"openapi":"3.1.0","info":{"title":"Beepable API","version":"1.0.0","summary":"Barcode pictures and decisions from a GTIN, a URL, or a SKU.","description":"Every route uses the same resolver. A valid GTIN becomes a retail barcode. An http or https URL, including a GS1 Digital Link, becomes a QR code. Anything else needs a symbology name.\n\n**Keys.** Create keys under Account. A *picture key* draws pictures and goes in the `k` query parameter, because a spreadsheet cannot set a header. An *All routes key* also opens the JSON, text, batch, CSV, and label routes. Send it as `Authorization: Bearer`. Never put an All routes key in an image address: that address is public.\n\n**Errors.** A JSON route answers `{\"ok\":false,\"error\":{\"code\":\"...\",\"message\":\"...\"}}`. A text or file route answers the sentence as the whole body. A picture route for a spreadsheet answers HTTP 200 with a PNG of the sentence. The sentences are exact.\n\nThe full reference, with limits and caching, is at https://beepable.app/docs. A short brief for an agent is at https://beepable.app/llms.txt.","contact":{"name":"Beepable","url":"https://beepable.app/contact"}},"servers":[{"url":"https://beepable.app"}],"externalDocs":{"description":"Guides for Excel, Google Sheets, CSV, and the API","url":"https://beepable.app/guides"},"tags":[{"name":"Barcodes","description":"One value in, one barcode or one decision out."},{"name":"Spreadsheets","description":"Text routes for Excel and Google Sheets. Most sheets need only the picture route, `/i/{symbology}.png`."},{"name":"Files and batches","description":"Up to 10,000 values per request. These return addresses, not pictures."},{"name":"GS1 Digital Link","description":"Build a GS1 Digital Link from a GTIN, then draw its QR code."},{"name":"Amazon FNSKU labels","description":"Print-ready Code 128 labels with the FNSKU, title, and condition."},{"name":"Reference","description":"Open routes that need no key."}],"paths":{"/i/{symbology}.png":{"get":{"tags":["Barcodes"],"operationId":"drawPictureInCell","summary":"Draw a picture for a spreadsheet cell","description":"The picture Excel and Google Sheets draw with `IMAGE`. Needs a picture key in `k`. Success is the same PNG as the resolved `/b/` route, with no `Location` header. With `k` in the address, success is cached for a year (`public, max-age=31536000, immutable`).\n\n```\n=IMAGE(\"https://beepable.app/i/auto.png?v=\"&ENCODEURL(A2)&\"&k=\"&$Z$1)\n```","security":[{"queryKey":[]},{"bearerKey":[]}],"parameters":[{"name":"symbology","in":"path","required":true,"description":"`auto` chooses from the value. Any other name draws that symbology or refuses the value. Names match without regard to case.","schema":{"type":"string","enum":["auto","ean8","upca","ean13","itf14","code128","qr","datamatrix","gs1datamatrix","fnsku"],"default":"auto"},"example":"auto"},{"name":"v","in":"query","required":true,"description":"The value to encode, URL-encoded. A space is `%20`, not `+`. A missing `v` is empty.","schema":{"type":"string","maxLength":2000},"example":"4006381333931"},{"name":"hrt","in":"query","description":"`1` prints the digits under an EAN-8, EAN-13, or UPC-A. Other symbologies ignore it.","schema":{"type":"string","enum":["1"]}}],"responses":{"200":{"description":"The barcode. A refused value, or a missing or revoked key, is still HTTP 200: a PNG of the sentence on a white card marked Not a barcode, with `Cache-Control: no-store`, so `IMAGE` can show it.","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"429":{"description":"Too many pictures had to be drawn at once.","content":{"text/plain":{"schema":{"type":"string"},"example":"Too many requests. Try again shortly."}},"headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}}}}},"/b/{symbology}.png":{"get":{"tags":["Barcodes"],"operationId":"drawPicture","summary":"Draw a picture","description":"Returns the PNG itself, never a redirect, because a redirect would break `IMAGE()`. `/b/auto.png` and the named route return the same bytes. Unlike `/i/`, a refused value is HTTP 400 with the sentence as text. A missing or revoked key is still a PNG card.","security":[{"queryKey":[]},{"bearerKey":[]}],"parameters":[{"name":"symbology","in":"path","required":true,"description":"`auto` chooses from the value. Any other name draws that symbology or refuses the value. Names match without regard to case.","schema":{"type":"string","enum":["auto","ean8","upca","ean13","itf14","code128","qr","datamatrix","gs1datamatrix","fnsku"],"default":"auto"},"example":"code128"},{"name":"v","in":"query","required":true,"description":"The value to encode, URL-encoded. A space is `%20`, not `+`. A missing `v` is empty.","schema":{"type":"string","maxLength":2000},"example":"SKU 44"},{"name":"hrt","in":"query","description":"`1` prints the digits under an EAN-8, EAN-13, or UPC-A. Other symbologies ignore it.","schema":{"type":"string","enum":["1"]}},{"name":"download","in":"query","description":"`1` adds `Content-Disposition: attachment` with the file name `{symbology}-{value}.png`. `dl=1` does the same.","schema":{"type":"string","enum":["1"]}}],"responses":{"200":{"description":"The barcode, or a PNG card for a missing or revoked key.","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"The resolver refused the value. The body is the sentence.","content":{"text/plain":{"schema":{"type":"string"},"example":"Check digit is invalid for this GTIN. Name code128 to encode it anyway."}}},"429":{"description":"Too many pictures had to be drawn at once.","content":{"text/plain":{"schema":{"type":"string"},"example":"Too many requests. Try again shortly."}},"headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}}}}},"/b/{symbology}.json":{"get":{"tags":["Barcodes"],"operationId":"decide","x-codeSamples":[{"lang":"JavaScript","source":"const response = await fetch(\"https://beepable.app/b/auto.json?v=4006381333931\", {\n  headers: { Authorization: `Bearer ${process.env.BEEPABLE_KEY}` },\n});\nconst result = await response.json();\nif (result.ok) console.log(result.symbology, result.imageUrl);\nelse console.log(result.error.code, result.error.message);"},{"lang":"Python","source":"import os, requests\n\nresponse = requests.get(\n    \"https://beepable.app/b/auto.json\",\n    params={\"v\": \"4006381333931\"},\n    headers={\"Authorization\": f\"Bearer {os.environ['BEEPABLE_KEY']}\"},\n)\nresult = response.json()\nprint(result[\"imageUrl\"] if result[\"ok\"] else result[\"error\"][\"message\"])"}],"summary":"Look up a value: barcode type and image address","description":"Looks up one value without drawing it: which barcode type it becomes, and the address of the picture. Needs an All routes key. `imageUrl` uses the resolved name, never `auto`, and has no key. Append `k` with a picture key before a sheet fetches it. `digitalLink` is true when the value is a GS1 Digital Link. That flag does not change the symbol: automatic mode still draws the URL as a QR code. Name `datamatrix` for a Data Matrix of the same URL.","security":[{"queryKey":[]},{"bearerKey":[]}],"parameters":[{"name":"symbology","in":"path","required":true,"description":"`auto` chooses from the value. Any other name draws that symbology or refuses the value. Names match without regard to case.","schema":{"type":"string","enum":["auto","ean8","upca","ean13","itf14","code128","qr","datamatrix","gs1datamatrix","fnsku"],"default":"auto"},"example":"auto"},{"name":"v","in":"query","required":true,"description":"The value to encode, URL-encoded. A space is `%20`, not `+`. A missing `v` is empty.","schema":{"type":"string","maxLength":2000},"example":"4006381333931"}],"responses":{"200":{"description":"The decision.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Decision"},"example":{"ok":true,"symbology":"ean13","digitalLink":false,"imageUrl":"https://beepable.app/b/ean13.png?v=4006381333931","print":{"minMm":29.832,"targetMm":37.29,"maxMm":74.58,"note":"EAN-13 nominal size is 37.29 × 25.93 mm at 100%. Magnification is 80–200%."}}}}},"400":{"description":"The resolver refused the value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"},"example":{"ok":false,"error":{"code":"bad_check_digit","message":"Check digit is invalid for this GTIN. Name code128 to encode it anyway."}}}}},"401":{"$ref":"#/components/responses/UnauthorizedJson"},"403":{"$ref":"#/components/responses/ForbiddenJson"},"429":{"$ref":"#/components/responses/RateLimitedJson"}}}},"/excel":{"get":{"tags":["Spreadsheets"],"operationId":"excelAddress","summary":"Get the image address as text (Excel WEBSERVICE)","description":"Needs an All routes key. Success is the image address as plain text, without a key. A refused value is still HTTP 200 with the sentence, so `WEBSERVICE` can show it, and is `no-store`. Append a picture key before `IMAGE` fetches the address.\n\n```\n=LET(r,WEBSERVICE(\"https://beepable.app/excel?v=\"&ENCODEURL(A2)&\"&s=auto&k=\"&$Z$3),IF(LEFT(r,4)=\"http\",IMAGE(r&\"&k=\"&$Z$1),r))\n```","security":[{"queryKey":[]},{"bearerKey":[]}],"parameters":[{"name":"v","in":"query","required":true,"description":"The value to encode, URL-encoded. A space is `%20`, not `+`. A missing `v` is empty.","schema":{"type":"string","maxLength":2000},"example":"4006381333931"},{"name":"s","in":"query","description":"The symbology. A missing `s` is `auto`.","schema":{"type":"string","enum":["auto","ean8","upca","ean13","itf14","code128","qr","datamatrix","gs1datamatrix","fnsku"],"default":"auto"}}],"responses":{"200":{"description":"The image address, or the sentence when the value is refused.","content":{"text/plain":{"schema":{"type":"string"},"example":"https://beepable.app/b/ean13.png?v=4006381333931"}}},"401":{"$ref":"#/components/responses/UnauthorizedText"},"403":{"$ref":"#/components/responses/ForbiddenText"},"429":{"$ref":"#/components/responses/RateLimitedText"}}}},"/sheets":{"get":{"tags":["Spreadsheets"],"operationId":"sheetsAddress","summary":"Get the image address as text (Apps Script)","description":"The same contract as `/excel`, for the Apps Script function `BARCODE`.","security":[{"queryKey":[]},{"bearerKey":[]}],"parameters":[{"name":"v","in":"query","required":true,"description":"The value to encode, URL-encoded. A space is `%20`, not `+`. A missing `v` is empty.","schema":{"type":"string","maxLength":2000},"example":"4006381333931"},{"name":"s","in":"query","description":"The symbology. A missing `s` is `auto`.","schema":{"type":"string","enum":["auto","ean8","upca","ean13","itf14","code128","qr","datamatrix","gs1datamatrix","fnsku"],"default":"auto"}}],"responses":{"200":{"description":"The image address, or the sentence when the value is refused.","content":{"text/plain":{"schema":{"type":"string"},"example":"https://beepable.app/b/ean13.png?v=4006381333931"}}},"401":{"$ref":"#/components/responses/UnauthorizedText"},"403":{"$ref":"#/components/responses/ForbiddenText"},"429":{"$ref":"#/components/responses/RateLimitedText"}}}},"/sheets/barcode.gs":{"get":{"tags":["Spreadsheets"],"operationId":"sheetsScript","summary":"Get the Apps Script source","description":"The Apps Script function `BARCODE(value, symbology, key)`. It returns the address, or throws the sentence. Paste it into Extensions, Apps Script.\n\n```\n=IMAGE(BARCODE(A2,\"auto\",$Z$3)&\"&k=\"&$Z$1)\n```","security":[],"responses":{"200":{"description":"The script.","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/api/batch":{"post":{"tags":["Files and batches"],"operationId":"batch","x-codeSamples":[{"lang":"JavaScript","source":"const response = await fetch(\"https://beepable.app/api/batch\", {\n  method: \"POST\",\n  headers: { Authorization: `Bearer ${process.env.BEEPABLE_KEY}`, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({ items: [{ value: \"4006381333931\" }, { value: \"SKU 44\", symbology: \"code128\" }] }),\n});\nconst { items } = await response.json();"},{"lang":"Python","source":"import os, requests\n\nresponse = requests.post(\n    \"https://beepable.app/api/batch\",\n    json={\"items\": [{\"value\": \"4006381333931\"}, {\"value\": \"SKU 44\", \"symbology\": \"code128\"}]},\n    headers={\"Authorization\": f\"Bearer {os.environ['BEEPABLE_KEY']}\"},\n)\nitems = response.json()[\"items\"]"}],"summary":"Look up to 10,000 values","description":"Needs an All routes key. Lists each item in order. The batch does not draw pictures. A JSON number is converted with `String`, so a GTIN that needs a leading zero must be a string. One bad item does not fail the batch.","security":[{"queryKey":[]},{"bearerKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchRequest"},"example":{"items":[{"value":"4006381333931"},{"value":"SKU 44","symbology":"code128"},{"digitalLink":{"gtin":"09506000134352","lot":"ABC123"}}]}}}},"responses":{"200":{"description":"One result per item, in order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResponse"},"example":{"ok":true,"items":[{"ok":true,"symbology":"ean13","imageUrl":"https://beepable.app/b/ean13.png?v=4006381333931"},{"ok":true,"symbology":"code128","imageUrl":"https://beepable.app/b/code128.png?v=SKU%2044"},{"ok":true,"symbology":"qr","imageUrl":"https://beepable.app/b/qr.png?v=https%3A%2F%2Fid.gs1.org%2F01%2F09506000134352%2F10%2FABC123","uri":"https://id.gs1.org/01/09506000134352/10/ABC123"}]}}}},"400":{"description":"The body is not an `items` array, has more than 10,000 items, or `pictureKey` is not a picture key of this account. An All routes key in `pictureKey` answers: Use a picture key, not an All routes key, in image addresses.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"},"example":{"ok":false,"error":{"code":"bad_request","message":"A request can contain at most 10,000 items."}}}}},"401":{"$ref":"#/components/responses/UnauthorizedJson"},"403":{"$ref":"#/components/responses/ForbiddenJson"},"413":{"description":"The body is larger than 2 MB.","content":{"text/plain":{"schema":{"type":"string"},"example":"This request is larger than 2 MB. Send fewer items per request."}}},"429":{"$ref":"#/components/responses/RateLimitedJson"}}}},"/csv":{"post":{"tags":["Files and batches"],"operationId":"csv","summary":"Add image addresses to a CSV","description":"Needs an All routes key. The header row needs `value` or `v`. `symbology` or `s` is optional. Other columns are kept. The response adds or replaces `image_url` and `error`. A valid `pk` also adds `image_formula` and puts `k` on each address. One bad row does not reject the file: that row has an empty address and the sentence. The file starts with a UTF-8 BOM.","security":[{"queryKey":[]},{"bearerKey":[]}],"parameters":[{"name":"pk","in":"query","description":"A picture key of the same account, added to each address.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"text/csv":{"schema":{"type":"string"},"example":"value,symbology\n4006381333931,\nSKU 44,code128\n"},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"pk":{"type":"string"}},"required":["file"]}}}},"responses":{"200":{"description":"The CSV, as the attachment `barcodes.csv`.","content":{"text/csv":{"schema":{"type":"string"}}}},"400":{"description":"A missing value column, more than 10,000 data rows, or a bad `pk`. The body is the sentence.","content":{"text/plain":{"schema":{"type":"string"},"example":"The CSV needs a value column."}}},"401":{"$ref":"#/components/responses/UnauthorizedText"},"403":{"$ref":"#/components/responses/ForbiddenText"},"413":{"description":"The file is larger than 5 MB.","content":{"text/plain":{"schema":{"type":"string"},"example":"This file is larger than 5 MB. Split it into smaller files."}}},"429":{"$ref":"#/components/responses/RateLimitedUpload"}}}},"/dl.json":{"get":{"tags":["GS1 Digital Link"],"operationId":"buildDigitalLink","summary":"Build a GS1 Digital Link","description":"Needs an All routes key. The path is `/01/`, then `/22/`, `/10/`, and `/21/`. Best before `15` and expiry `17` are query parameters, in that order. `eanUrl` is EAN-8, UPC-A, EAN-13, or ITF-14 from the original GTIN length. `gs1DataMatrixUrl` draws the element string as a GS1 DataMatrix.","security":[{"queryKey":[]},{"bearerKey":[]}],"parameters":[{"name":"gtin","in":"query","required":true,"description":"A GTIN of 8, 12, 13, or 14 digits with a valid check digit.","schema":{"type":"string"},"example":"09506000134352"},{"name":"lot","in":"query","description":"Batch or lot, AI (10). 1 to 20 characters.","schema":{"type":"string","maxLength":20}},{"name":"serial","in":"query","description":"Serial number, AI (21). 1 to 20 characters.","schema":{"type":"string","maxLength":20}},{"name":"cpv","in":"query","description":"Consumer product variant, AI (22). 1 to 20 characters.","schema":{"type":"string","maxLength":20}},{"name":"exp","in":"query","description":"Expiry date, AI (17). `YYMMDD` or `YYYY-MM-DD`.","schema":{"type":"string"}},{"name":"bb","in":"query","description":"Best before date, AI (15). `YYMMDD` or `YYYY-MM-DD`.","schema":{"type":"string"}},{"name":"domain","in":"query","description":"An https address for the link. The default is `https://id.gs1.org`.","schema":{"type":"string","format":"uri"}}],"responses":{"200":{"description":"The link and the image addresses.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DigitalLink"},"example":{"ok":true,"uri":"https://id.gs1.org/01/09506000134352/10/ABC123?17=271231","elementString":"(01)09506000134352(17)271231(10)ABC123","qrUrl":"https://beepable.app/b/qr.png?v=https%3A%2F%2Fid.gs1.org%2F01%2F09506000134352%2F10%2FABC123%3F17%3D271231","eanUrl":"https://beepable.app/b/itf14.png?v=09506000134352","gs1DataMatrixUrl":"https://beepable.app/b/gs1datamatrix.png?v=(01)09506000134352(17)271231(10)ABC123","print":{"minMm":16.236,"targetMm":20.295,"maxMm":40.59,"note":"QR minimum 16.236 mm, target 20.295 mm, maximum 40.59 mm. The X-dimension is 0.396 mm, 0.495 mm, and 0.990 mm, including a quiet zone of 4 modules on every side."}}}}},"400":{"description":"A field is refused.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"},"example":{"ok":false,"error":{"code":"bad_check_digit","message":"The check digit is invalid for this GTIN."}}}}},"401":{"$ref":"#/components/responses/UnauthorizedJson"},"403":{"$ref":"#/components/responses/ForbiddenJson"},"429":{"$ref":"#/components/responses/RateLimitedJson"}}}},"/i/dl.png":{"get":{"tags":["GS1 Digital Link"],"operationId":"drawDigitalLink","summary":"Draw the QR code of a Digital Link","description":"Needs a picture key in `k`. Takes the same fields as `/dl.json` and draws the QR code of `uri`. The bytes match `/b/qr.png` for that URI.\n\n```\n=IMAGE(\"https://beepable.app/i/dl.png?gtin=\"&ENCODEURL(A2)&\"&lot=\"&ENCODEURL(B2)&\"&exp=\"&ENCODEURL(C2)&\"&domain=\"&ENCODEURL($Z$2)&\"&k=\"&$Z$1)\n```\n\nA sheet can also build the path as `=\"https://id.gs1.org/01/\"&TEXT(A2,\"00000000000000\")`. That formula does not check the check digit. Use `/i/dl.png` when the digit must be checked.","security":[{"queryKey":[]},{"bearerKey":[]}],"parameters":[{"name":"gtin","in":"query","required":true,"description":"A GTIN of 8, 12, 13, or 14 digits with a valid check digit.","schema":{"type":"string"},"example":"09506000134352"},{"name":"lot","in":"query","description":"Batch or lot, AI (10). 1 to 20 characters.","schema":{"type":"string","maxLength":20}},{"name":"serial","in":"query","description":"Serial number, AI (21). 1 to 20 characters.","schema":{"type":"string","maxLength":20}},{"name":"cpv","in":"query","description":"Consumer product variant, AI (22). 1 to 20 characters.","schema":{"type":"string","maxLength":20}},{"name":"exp","in":"query","description":"Expiry date, AI (17). `YYMMDD` or `YYYY-MM-DD`.","schema":{"type":"string"}},{"name":"bb","in":"query","description":"Best before date, AI (15). `YYMMDD` or `YYYY-MM-DD`.","schema":{"type":"string"}},{"name":"domain","in":"query","description":"An https address for the link. The default is `https://id.gs1.org`.","schema":{"type":"string","format":"uri"}}],"responses":{"200":{"description":"The QR code. A refused value, or a missing or revoked key, is still HTTP 200: a PNG of the sentence on a white card marked Not a barcode, with `Cache-Control: no-store`, so `IMAGE` can show it.","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"429":{"description":"Too many pictures had to be drawn at once.","content":{"text/plain":{"schema":{"type":"string"},"example":"Too many requests. Try again shortly."}},"headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}}}}},"/dl/pack.zip":{"get":{"tags":["GS1 Digital Link"],"operationId":"digitalLinkPack","summary":"Download the retail barcode and the QR code","description":"Needs a picture key. A ZIP named `{gtin}-digital-link.zip` with `{gtin}-ean.png`, `{gtin}-qr.png`, and `README.txt` with print sizes.","security":[{"queryKey":[]},{"bearerKey":[]}],"parameters":[{"name":"gtin","in":"query","required":true,"description":"A GTIN of 8, 12, 13, or 14 digits with a valid check digit.","schema":{"type":"string"},"example":"09506000134352"},{"name":"lot","in":"query","description":"Batch or lot, AI (10). 1 to 20 characters.","schema":{"type":"string","maxLength":20}},{"name":"serial","in":"query","description":"Serial number, AI (21). 1 to 20 characters.","schema":{"type":"string","maxLength":20}},{"name":"cpv","in":"query","description":"Consumer product variant, AI (22). 1 to 20 characters.","schema":{"type":"string","maxLength":20}},{"name":"exp","in":"query","description":"Expiry date, AI (17). `YYMMDD` or `YYYY-MM-DD`.","schema":{"type":"string"}},{"name":"bb","in":"query","description":"Best before date, AI (15). `YYMMDD` or `YYYY-MM-DD`.","schema":{"type":"string"}},{"name":"domain","in":"query","description":"An https address for the link. The default is `https://id.gs1.org`.","schema":{"type":"string","format":"uri"}}],"responses":{"200":{"description":"The ZIP attachment.","content":{"application/zip":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"A field is refused. The body is the sentence.","content":{"text/plain":{"schema":{"type":"string"},"example":"The check digit is invalid for this GTIN."}}},"401":{"$ref":"#/components/responses/UnauthorizedText"},"429":{"$ref":"#/components/responses/RateLimitedText"}}}},"/dl.csv":{"post":{"tags":["GS1 Digital Link"],"operationId":"digitalLinkCsv","summary":"Build Digital Links for a CSV","description":"Needs an All routes key. The header accepts `gtin`, `ean`, `upc`, or `value`, plus `lot` or `batch`, `serial`, `expiry`, `exp`, or `17`, `best_before` or `15`, and `domain`. A query or multipart `domain` is used when that column is missing or the cell is empty. The response adds `digital_link`, `element_string`, `qr_url`, `ean_url`, and `error`, as the attachment `digital-links.csv`.","security":[{"queryKey":[]},{"bearerKey":[]}],"parameters":[{"name":"pk","in":"query","description":"A picture key of the same account, added to the two image addresses.","schema":{"type":"string"}},{"name":"domain","in":"query","description":"The default https domain for rows without one.","schema":{"type":"string","format":"uri"}}],"requestBody":{"required":true,"content":{"text/csv":{"schema":{"type":"string"},"example":"gtin,lot,expiry\n09506000134352,ABC123,2027-12-31\n"},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"pk":{"type":"string"},"domain":{"type":"string"}},"required":["file"]}}}},"responses":{"200":{"description":"The CSV attachment.","content":{"text/csv":{"schema":{"type":"string"}}}},"400":{"description":"A missing GTIN column, too many rows, or a bad `pk`. The body is the sentence.","content":{"text/plain":{"schema":{"type":"string"},"example":"The CSV needs a gtin column."}}},"401":{"$ref":"#/components/responses/UnauthorizedText"},"403":{"$ref":"#/components/responses/ForbiddenText"},"413":{"description":"The file is larger than 5 MB.","content":{"text/plain":{"schema":{"type":"string"},"example":"This file is larger than 5 MB. Split it into smaller files."}}},"429":{"$ref":"#/components/responses/RateLimitedUpload"}}}},"/l/fnsku.png":{"get":{"tags":["Amazon FNSKU labels"],"operationId":"drawLabel","summary":"Draw one FNSKU label","description":"Needs a picture key in `k`. A public cache applies only when `k` is in the query.\n\n```\n=IMAGE(\"https://beepable.app/l/fnsku.png?v=\"&ENCODEURL(A2)&\"&t=\"&ENCODEURL(B2)&\"&c=new&k=\"&$Z$1)\n```","security":[{"queryKey":[]},{"bearerKey":[]}],"parameters":[{"name":"v","in":"query","required":true,"description":"The FNSKU. Copy it from Seller Central.","schema":{"type":"string"},"example":"X00SAMPLE01"},{"name":"t","in":"query","description":"The product title printed under the bars. At most 500 characters.","schema":{"type":"string","maxLength":500},"example":"Sample product title"},{"name":"c","in":"query","description":"The condition. A missing `c` is New.","schema":{"type":"string","default":"New"},"example":"New"},{"name":"set","in":"query","description":"`1` adds the sold-as-set line.","schema":{"type":"string","enum":["0","1"]}},{"name":"tpl","in":"query","description":"The label template. The server default is `avery-5160`.","schema":{"type":"string","enum":["avery-5160","thermal-2x1","thermal-2.25x1.25","a4-21-63x38","a4-24-63x34","a4-24-70x37"],"default":"avery-5160"}}],"responses":{"200":{"description":"The label. A refused value, or a missing or revoked key, is still HTTP 200: a PNG of the sentence on a white card marked Not a barcode, with `Cache-Control: no-store`, so `IMAGE` can show it.","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"429":{"description":"Too many pictures had to be drawn at once.","content":{"text/plain":{"schema":{"type":"string"},"example":"Too many requests. Try again shortly."}},"headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}}}}},"/l/fnsku.pdf":{"get":{"tags":["Amazon FNSKU labels"],"operationId":"printLabel","summary":"Print copies of one label as a PDF","description":"Needs a picture key. Returns one PDF named `fnsku-{FNSKU}.pdf`, `private, no-store`. Print at 100% scale.","security":[{"queryKey":[]},{"bearerKey":[]}],"parameters":[{"name":"v","in":"query","required":true,"description":"The FNSKU. Copy it from Seller Central.","schema":{"type":"string"},"example":"X00SAMPLE01"},{"name":"t","in":"query","description":"The product title printed under the bars. At most 500 characters.","schema":{"type":"string","maxLength":500},"example":"Sample product title"},{"name":"c","in":"query","description":"The condition. A missing `c` is New.","schema":{"type":"string","default":"New"},"example":"New"},{"name":"set","in":"query","description":"`1` adds the sold-as-set line.","schema":{"type":"string","enum":["0","1"]}},{"name":"tpl","in":"query","description":"The label template. The server default is `avery-5160`.","schema":{"type":"string","enum":["avery-5160","thermal-2x1","thermal-2.25x1.25","a4-21-63x38","a4-24-63x34","a4-24-70x37"],"default":"avery-5160"}},{"name":"qty","in":"query","description":"Copies, from 1 to 1,000.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":1}},{"name":"start","in":"query","description":"The first position on the sheet, from 1. A roll has no start position.","schema":{"type":"integer","minimum":1,"default":1}}],"responses":{"200":{"description":"The PDF attachment.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"A field is refused. The body is the sentence.","content":{"text/plain":{"schema":{"type":"string"},"example":"Quantity must be a whole number from 1 to 1,000."}}},"401":{"$ref":"#/components/responses/UnauthorizedText"},"429":{"$ref":"#/components/responses/RateLimitedText"},"503":{"description":"The PDF took too long.","content":{"text/plain":{"schema":{"type":"string"},"example":"This file took too long to process. Split it into smaller files."}}}}}},"/labels/fnsku.pdf":{"post":{"tags":["Amazon FNSKU labels"],"operationId":"printLabels","summary":"Print labels from a file","description":"Needs an All routes key or a signed-in session. A picture key is HTTP 403. The body is CSV, a Seller Central tab-separated file, or JSON. CSV headers match without regard to case: fnsku, fulfillment network sku, fn sku, title, product-name, product name, item-name, item name, condition, item-condition, qty, quantity, units, quantity-to-label, units to send, set, sold-as-set. The PDF holds the good rows. Bad rows stay out, and `x-label-errors` counts them.","security":[{"queryKey":[]},{"bearerKey":[]}],"parameters":[{"name":"tpl","in":"query","description":"The label template.","schema":{"type":"string","enum":["avery-5160","thermal-2x1","thermal-2.25x1.25","a4-21-63x38","a4-24-63x34","a4-24-70x37"],"default":"avery-5160"}},{"name":"start","in":"query","description":"The first position on the first sheet.","schema":{"type":"integer","minimum":1,"default":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabelJob"},"example":{"template":"avery-5160","start":1,"items":[{"fnsku":"X00SAMPLE01","title":"Sample product title","condition":"New","qty":1,"set":false}]}},"text/csv":{"schema":{"type":"string"},"example":"fnsku,title,condition,qty\nX00SAMPLE01,Sample product title,New,30\n"}}},"responses":{"200":{"description":"The PDF attachment `fnsku-labels.pdf`.","headers":{"x-label-errors":{"description":"The number of rows left out.","schema":{"type":"integer"}}},"content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"The file has no usable rows. The body is the sentence.","content":{"text/plain":{"schema":{"type":"string"},"example":"Every row failed. Fix the FNSKU, condition, or quantity and try again."}}},"401":{"$ref":"#/components/responses/UnauthorizedText"},"403":{"$ref":"#/components/responses/ForbiddenText"},"413":{"description":"The file is larger than 5 MB.","content":{"text/plain":{"schema":{"type":"string"},"example":"This file is larger than 5 MB. Split it into smaller files."}}},"429":{"$ref":"#/components/responses/RateLimitedUpload"},"503":{"description":"The PDF took too long.","content":{"text/plain":{"schema":{"type":"string"},"example":"This file took too long to process. Split it into smaller files."}}}}}},"/labels/fnsku.csv":{"post":{"tags":["Amazon FNSKU labels"],"operationId":"checkLabels","summary":"Check a label file and add label addresses","description":"Needs an All routes key or a signed-in session. Keeps the input rows and adds `label_url`, `image_formula`, and `error`. `pk` or JSON `pictureKey` must be a picture key of the same account: it puts `k` on `label_url` and fills `image_formula`. The file starts with a UTF-8 BOM.","security":[{"queryKey":[]},{"bearerKey":[]}],"parameters":[{"name":"pk","in":"query","description":"A picture key of the same account.","schema":{"type":"string"}},{"name":"tpl","in":"query","description":"The label template.","schema":{"type":"string","enum":["avery-5160","thermal-2x1","thermal-2.25x1.25","a4-21-63x38","a4-24-63x34","a4-24-70x37"],"default":"avery-5160"}}],"requestBody":{"required":true,"content":{"text/csv":{"schema":{"type":"string"},"example":"fnsku,title,condition\nX00SAMPLE01,Sample product title,New\n"},"application/json":{"schema":{"$ref":"#/components/schemas/LabelJob"}}}},"responses":{"200":{"description":"The CSV attachment `fnsku-labels.csv`.","content":{"text/csv":{"schema":{"type":"string"}}}},"400":{"description":"A missing fnsku column, no rows, or a bad key. The body is the sentence.","content":{"text/plain":{"schema":{"type":"string"},"example":"The CSV needs an fnsku column."}}},"401":{"$ref":"#/components/responses/UnauthorizedText"},"403":{"$ref":"#/components/responses/ForbiddenText"},"413":{"description":"The file is larger than 5 MB.","content":{"text/plain":{"schema":{"type":"string"},"example":"This file is larger than 5 MB. Split it into smaller files."}}},"429":{"$ref":"#/components/responses/RateLimitedUpload"}}}},"/api":{"get":{"tags":["Reference"],"operationId":"index","summary":"List the routes","security":[],"responses":{"200":{"description":"Route names and paths.","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}}}}},"/openapi.json":{"get":{"tags":["Reference"],"operationId":"openapi","summary":"Get this OpenAPI document","security":[],"responses":{"200":{"description":"OpenAPI 3.1.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/sample/{file}":{"get":{"tags":["Reference"],"operationId":"sample","summary":"Get an open sample picture","description":"Open pictures for pages and tests: `ean13.png` is 4006381333931, `dl-ean.png` is the ITF-14 of 09506000134352, `dl-qr.png` is the QR code of https://id.gs1.org/01/09506000134352, and `fnsku-label.png` is X00SAMPLE01, Sample product title, New.","security":[],"parameters":[{"name":"file","in":"path","required":true,"schema":{"type":"string","enum":["ean13.png","dl-ean.png","dl-qr.png","fnsku-label.png"]}}],"responses":{"200":{"description":"The sample.","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}}}}}},"components":{"securitySchemes":{"queryKey":{"type":"apiKey","in":"query","name":"k","description":"A key in the `k` query parameter. Spreadsheets cannot set a header, so picture routes take the key here. Put only a picture key in an image address, because the address is public."},"bearerKey":{"type":"http","scheme":"bearer","description":"A key in `Authorization: Bearer`. Use an All routes key for JSON, text, batch, CSV, and label routes. A response without `k` in the address is `private, no-store`."}},"headers":{"Retry-After":{"description":"Seconds to wait before you try again.","schema":{"type":"integer"}},"RateLimit":{"description":"Remaining quota, from the IETF draft. Example: `\"render\";r=0;t=67`.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The quota policy, from the IETF draft. Example: `\"render\";q=20000;w=67`.","schema":{"type":"string"}}},"responses":{"UnauthorizedJson":{"description":"No key, an unknown key, or a revoked key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"},"example":{"ok":false,"error":{"code":"unauthorized","message":"This request needs an API key."}}}}},"ForbiddenJson":{"description":"A picture key on a route that needs an All routes key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"},"example":{"ok":false,"error":{"code":"forbidden","message":"This key can only draw pictures."}}}}},"RateLimitedJson":{"description":"Too many requests.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"},"example":{"ok":false,"error":{"code":"rate_limited","message":"Too many requests. Try again shortly."}}}},"headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"UnauthorizedText":{"description":"No key, an unknown key, or a revoked key.","content":{"text/plain":{"schema":{"type":"string"},"example":"This request needs an API key."}}},"ForbiddenText":{"description":"A picture key on a route that needs an All routes key.","content":{"text/plain":{"schema":{"type":"string"},"example":"This key can only draw pictures."}}},"RateLimitedText":{"description":"Too many requests.","content":{"text/plain":{"schema":{"type":"string"},"example":"Too many requests. Try again shortly."}},"headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"RateLimitedUpload":{"description":"Too many uploads, or another file is still running. The sentence is one of: Too many uploads. Try again in a minute. Another file is still being processed. Wait for it to finish.","content":{"text/plain":{"schema":{"type":"string"},"example":"Too many uploads. Try again in a minute."}},"headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}}},"schemas":{"Symbology":{"type":"string","enum":["ean8","upca","ean13","itf14","code128","qr","datamatrix","gs1datamatrix","fnsku"],"description":"A resolved symbology name. Never `auto`."},"PrintSize":{"type":"object","required":["minMm","targetMm","maxMm","note"],"properties":{"minMm":{"type":"number","description":"The smallest width that scans reliably, in millimetres."},"targetMm":{"type":"number","description":"The nominal width, in millimetres."},"maxMm":{"type":"number","description":"The largest width in the specification, in millimetres."},"note":{"type":"string"}}},"Decision":{"type":"object","required":["ok","symbology","digitalLink","imageUrl","print"],"properties":{"ok":{"const":true},"symbology":{"$ref":"#/components/schemas/Symbology"},"digitalLink":{"type":"boolean","description":"True when the value is a GS1 Digital Link. It does not change the symbol."},"imageUrl":{"type":"string","format":"uri","description":"The `/b/` address of the picture, without a key."},"print":{"$ref":"#/components/schemas/PrintSize"}}},"ErrorBody":{"type":"object","required":["ok","error"],"properties":{"ok":{"const":false},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["empty","too_long","not_gtin_or_url","not_fnsku","wrong_length","bad_check_digit","bad_check_digit_in_link","symbology_mismatch","unsupported_characters","cannot_encode","unknown_symbology","bad_element_string","bad_domain","bad_lot","bad_serial","bad_cpv","bad_date","bad_request","unauthorized","forbidden","rate_limited","internal"]},"message":{"type":"string","description":"The exact sentence. Show it to the person who entered the value."}}}}},"DigitalLinkFields":{"type":"object","required":["gtin"],"properties":{"gtin":{"type":"string"},"lot":{"type":"string"},"serial":{"type":"string"},"cpv":{"type":"string"},"expiry":{"type":"string","description":"Also accepted as `exp`."},"bestBefore":{"type":"string","description":"Also accepted as `bb`."},"domain":{"type":"string","format":"uri"}}},"BatchItem":{"oneOf":[{"type":"object","title":"A value","required":["value"],"properties":{"value":{"type":["string","number"],"description":"A GTIN that needs a leading zero must be a string."},"symbology":{"type":"string","enum":["auto","ean8","upca","ean13","itf14","code128","qr","datamatrix","gs1datamatrix","fnsku"],"default":"auto"}}},{"type":"object","title":"A Digital Link","required":["digitalLink"],"properties":{"digitalLink":{"$ref":"#/components/schemas/DigitalLinkFields"}}}]},"BatchRequest":{"type":"object","required":["items"],"properties":{"items":{"type":"array","maxItems":10000,"items":{"$ref":"#/components/schemas/BatchItem"}},"pictureKey":{"type":"string","description":"A picture key of the same account. It adds `k` to each `imageUrl` and a `formula` to each good item."}}},"BatchResult":{"oneOf":[{"type":"object","title":"A good item","required":["ok","symbology","imageUrl"],"properties":{"ok":{"const":true},"symbology":{"$ref":"#/components/schemas/Symbology"},"imageUrl":{"type":"string","format":"uri"},"formula":{"type":"string","description":"Present when `pictureKey` is set."},"uri":{"type":"string","format":"uri","description":"Present for a Digital Link item."}}},{"$ref":"#/components/schemas/ErrorBody"}]},"BatchResponse":{"type":"object","required":["ok","items"],"properties":{"ok":{"const":true},"items":{"type":"array","items":{"$ref":"#/components/schemas/BatchResult"}}}},"DigitalLink":{"type":"object","required":["ok","uri","elementString","qrUrl","eanUrl","gs1DataMatrixUrl","print"],"properties":{"ok":{"const":true},"uri":{"type":"string","format":"uri"},"elementString":{"type":"string"},"qrUrl":{"type":"string","format":"uri"},"eanUrl":{"type":"string","format":"uri"},"gs1DataMatrixUrl":{"type":"string","format":"uri"},"print":{"$ref":"#/components/schemas/PrintSize"}}},"LabelItem":{"type":"object","required":["fnsku"],"properties":{"fnsku":{"type":"string"},"title":{"type":"string","maxLength":500},"condition":{"type":"string","default":"New","description":"One of New, Used - Like New, Used - Very Good, Used - Good, Used - Acceptable, Collectible, or Refurbished."},"qty":{"type":"integer","minimum":1,"maximum":1000,"default":1},"set":{"type":"boolean","default":false}}},"LabelJob":{"type":"object","required":["items"],"properties":{"template":{"type":"string","enum":["avery-5160","thermal-2x1","thermal-2.25x1.25","a4-21-63x38","a4-24-63x34","a4-24-70x37"],"default":"avery-5160"},"start":{"type":"integer","minimum":1,"default":1},"pictureKey":{"type":"string"},"items":{"type":"array","items":{"$ref":"#/components/schemas/LabelItem"},"description":"The JSON needs an items array. The file has no label rows."}}}}},"x-error-sentences":{"empty":"Enter a GTIN or a URL.","too_long":"Value is longer than 2,000 characters.","not_gtin_or_url":"This is neither a valid GTIN, an Amazon FNSKU, nor a URL. Name a symbology, such as code128.","not_fnsku":"An Amazon FNSKU has 10 characters and usually starts with X00. Copy it from Seller Central.","bad_check_digit":"Check digit is invalid for this GTIN. Name code128 to encode it anyway.","bad_check_digit_in_link":"The GS1 identifier in this Digital Link has an invalid check digit. Name qr to encode the URL anyway.","unsupported_characters":"Code 128 can only encode basic Latin characters.","cannot_encode":"Code 128 can encode at most 500 characters.","unknown_symbology":"Unknown symbology. Use auto, ean8, upca, ean13, itf14, code128, qr, datamatrix, gs1datamatrix, or fnsku.","bad_element_string":"The element string must start with (01) and a 14-digit GTIN, and use only the AIs this service checks.","wrong_length":"A retail barcode needs 8, 12, 13, or 14 digits. This has {n}. Name code128 to encode it anyway.","symbology_mismatch":"{Name} needs {n} digits. This has {count}.","bad_domain":"The domain must be an https address, such as https://id.brand.com.","bad_lot":"The lot must be 1 to 20 characters from GS1 character set 82. Use digits, letters, and !\"%&'()*+,-./:;<=>?_.","bad_serial":"The serial must be 1 to 20 characters from GS1 character set 82. Use digits, letters, and !\"%&'()*+,-./:;<=>?_.","bad_cpv":"The CPV must be 1 to 20 characters from GS1 character set 82. Use digits, letters, and !\"%&'()*+,-./:;<=>?_.","bad_date":"The date must be YYMMDD or YYYY-MM-DD. Use a month from 01 to 12. The day may be 00.","unauthorized":"This request needs an API key.","forbidden":"This key can only draw pictures.","rate_limited":"Too many requests. Try again shortly."}}