Gérer et explorer
Manage products with the REST API
Connect your product catalog, packaging workflow or other server applications to CodePersist. Create and update products, publish their links and retrieve QR artwork through a dedicated customer API.
- Connect securely
Create an API key for one organisation and keep its secret on your server.
- Save your product
Create or update the product, then read the exact version you want customers to use.
- Publish and print
Publish that version, generate its SVG artwork and check a scan before printing.
The API uses the same product creation, version history, publication and artwork services as the app. Saving does not publish. The same plan capacity and product availability rules apply in both.
Access and API keys
L’API client exige Lite ou Premium. Les propriétaires créent et révoquent les clés de l’organisation dans l’application. Chaque clé reste liée à son organisation d’origine et suit l’accès actuel de son créateur : une suspension bloque son utilisation, et le retrait ou le déplacement du créateur supprime ses anciennes clés. Une fermeture ou une limite d’utilisateurs non résolue bloque aussi le travail par API.
Create a key and save its secret
- Open API keys in the organisation you want to connect. Use a separate, clearly named key for each integration so you can identify and revoke it later.
- Choose read-only access for reporting and downloads, or read/write access when the integration needs to change products. Read-only is the default.
- Choose an expiration. The app offers 30 days, 90 days or one year; 90 days is the default.
- Create the key and copy the secret into your server's secret store before dismissing it. Up to 20 active, unexpired keys can exist per organisation.
Copy the secret when it is created: it is shown only once and cannot be recovered. Keep it in your server's secret store, send it in the Authorization header over HTTPS and keep it out of URLs, browser applications and logs. JSON requests use Content-Type: application/json. Replace YOUR_API_KEY below with your secret.
https://api.codepersist.com/api/v1
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Accept: application/json
Choose the access your integration needs
Read-only keys can list and read products, versions and publication history, preview saved QR artwork and download existing artwork. Read/write keys can also create, update, publish, disable, enable and permanently delete products, and permanently attach an already-connected custom domain. Keys cannot manage billing or other API keys, or connect, verify, list or remove domains.
Each key belongs to one organisation. All reads and changes stay within that organisation, including batch items. A browser cookie does not grant customer API access or change which organisation a key can access.
Plan for key expiration
The expiration starts when the key is created. Replace it before the displayed expiration time to keep the integration running. Expired keys cannot start new requests and return 401. Expiration does not unpublish or disable products that the integration already managed; their printed QR codes keep their existing availability.
Replace or revoke a key
- Create a replacement key in the same organisation with the permissions your integration needs.
- Update your integration's secret store and verify a request with the replacement key.
- Revoke the old key in the app after the integration is using the replacement. Revocation cannot be undone; work already accepted may finish within its original organisation.
- If a secret is exposed, revoke it immediately. Create a replacement and update the integration; the old secret cannot be recovered.
Actions are attributed to the key creator. A change to that creator's organisation invalidates the key. Downgrading to Free blocks API use and key creation, while key listing and revocation remain available in the app. An unexpired, unrevoked key works again after returning to a paid tier.
Create, update and publish
The following examples show HTTP methods, paths and request bodies. Send the authentication header with every request. Replace the uppercase ID placeholders with the UUIDs returned by the API, and use your own GTIN and destination URL.
Request formats and required fields
Use the exact camelCase field and query names in the reference. Unknown or duplicate JSON fields, unknown or repeated query parameters and empty query values return 400. Send numeric fields as JSON numbers, not quoted strings, and booleans as true or false. UUIDs must be nonzero and use the 36-character hyphenated form; either letter case is accepted. Route and query integers use decimal digits without signs, spaces, fractions or exponents. GET, DELETE and single-product disable requests have no body.
Create requires name, gtin and destinationUrl. The name must contain 1–200 characters after trimming; the GTIN must have 8, 12, 13 or 14 digits and a valid check digit. destinationUrl must be an absolute HTTP or HTTPS URL without credentials, up to 2048 characters after normalization. Optional resolverUrl, resources and barcode may be omitted or null to select their defaults. PUT requires expectedVersionId, name, complete resources and barcode. Nested resource fields must be present, including both booleans and empty arrays; a link's slotKey and mediaType may be null but must still be included. Use the OpenAPI schemas for every field's bounds and rules.
1. Create an unpublished product
A successful creation returns 201, a Location header and the product document. Save its id and currentVersion.id. Omitting resolverUrl or sending null uses the standard CodePersist resolver. A custom resolver must be an available HTTPS origin owned by your organisation, with an ASCII hostname and no path, query, fragment or credentials; use punycode for internationalized hostnames. The GTIN and original resolver remain fixed. A product created on the CodePersist address can later gain one permanent custom-domain attachment through the customer API, app or Excel import, while keeping its original address. destinationUrl remains required even when you supply resources.
POST /api/v1/products
{
"name": "SKU-001",
"gtin": "09506000134352",
"destinationUrl": "https://brand.example/products/sku-001"
}
2. Read before replacing configuration
Read the product, then send expectedVersionId from currentVersion.id. PUT replaces the editable configuration: name, complete resources and barcode settings. This server-side JavaScript example changes the name while preserving the full resource graph and QR settings. Supply apiKey and productId from your integration. When editing links, preserve every other link, slot and descriptor. Saving creates a new immutable version and does not publish it.
const apiBase = "https://api.codepersist.com/api/v1";
const headers = {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json"
};
const response = await fetch(`${apiBase}/products/${productId}`, { headers });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const product = await response.json();
const saved = await fetch(`${apiBase}/products/${productId}`, {
method: "PUT",
headers,
body: JSON.stringify({
expectedVersionId: product.currentVersion.id,
name: "SKU-001-v2",
resources: product.currentVersion.resources,
barcode: product.currentVersion.barcode
})
});
if (!saved.ok) throw new Error(`HTTP ${saved.status}`);
const updatedProduct = await saved.json();
Attach a connected custom domain with the API
This optional action needs a read/write key and an enabled product created on the CodePersist resolver with no custom attachment. First connect and verify your domain in the app and wait for Active. Read the product, then send its currentVersion.id and the domain's full HTTPS origin to POST /products/{id}/custom-domain. Both fields are required and cannot be null. Use an ASCII hostname without a path, query, fragment or credentials; the normalized origin must match the connected domain. No internal domain ID is needed.
POST /api/v1/products/PRODUCT_ID/custom-domain
{
"expectedVersionId": "CURRENT_VERSION_ID",
"resolverUrl": "https://id.brand.example/"
}
POST /api/v1/products/PRODUCT_ID/publications
{ "expectedVersionId": "VERSION_ID_FROM_ATTACHMENT_RESPONSE" }
A successful attachment returns 200 and an updated product with a new currentVersion.id. It is permanent: you cannot detach, replace or add another custom domain. It uses the same product slot and keeps resolverUrl as the original origin. digitalLinkUrl and new previews use the custom address immediately. Publish the returned version separately to update both addresses, then generate new production artwork. Previously published output and printed CodePersist codes keep their original address. The API does not connect, verify or manage domain hosting.
A missing or foreign product returns 404. A missing, foreign or unavailable domain returns a generic 400. A second attachment returns 400 with product.customDomain.alreadyAssigned. A stale version, disabled product or blocked product or plan operation returns 409. After an uncertain response, read the product and its current artwork URL before retrying; repeating an attachment is not treated as a successful no-op.
Utilisez votre domaine dans vos codes QR3. Publish the saved version
Use the new currentVersion.id after a save. Publishing makes that exact version available to scans at the original address and any attached custom domain. A successful request returns 200 and the product's publication state. A newer save can require a separate publication. A stale expectedVersionId returns 409 so an integration cannot silently publish an unreviewed edit.
POST /api/v1/products/PRODUCT_ID/publications
{ "expectedVersionId": "CURRENT_VERSION_ID" }
4. Generate and download SVG artwork
Production artwork requires the exact current version to be published and enabled. Generation returns 201 with an id, versionId, downloadUrl and sha256. Download the returned URL to retrieve the retained SVG; X-Content-SHA256 identifies the content. Use barcode-preview with the same expectedVersionId to obtain a temporary preview before publishing. The product's digitalLinkUrl is the current artwork URL: it changes after the customer API, app or Excel import permanently attaches a custom domain. resolverUrl keeps the original origin, and earlier retained artwork keeps its original URL. New previews use the attachment immediately; new production artwork requires publication.
POST /api/v1/products/PRODUCT_ID/barcodes
{ "expectedVersionId": "CURRENT_VERSION_ID" }
GET /api/v1/barcodes/BARCODE_ID/svg
Accept: image/svg+xml
Which identifier belongs in each request?
- product.id selects the product. Use it in a /products/{id} path or a batch item's productId.
- currentVersion.id identifies the exact saved version. Copy it to expectedVersionId when editing, attaching a custom domain, publishing, restoring or generating artwork.
- A historical version's number selects which content to restore. Use a positive whole number no greater than 9223372036854775807. Restoration creates a new current version and needs a separate publication.
- removalOperationId identifies the disable operation. Copy it to expectedRemovalOperationId when enabling, retrying removal or deleting.
- Copy these values from API responses. The key determines the organisation; supplying another organisation's identifier never changes its access.
Preserve links, languages and QR settings
PUT replaces the complete editable configuration. Begin with the product response and preserve resource IDs, all links and slots, relation URIs, languages, media types and context unless you intend to change them. Keep 1–32 links, including product-page with the exact GS1 pip relation, and at most 16 slots. The default must refer to an existing link; assigned links must include their slot's relation and satisfy its minimum and maximum. Sending only the link you edited can remove other destinations. Relation fragments are significant. Use the OpenAPI schemas for all limits and supported metadata formats, and the links and languages guide to understand how scan destinations are chosen.
V1 produces GS1 Digital Link QR artwork in SVG format. barcode.errorCorrection accepts only uppercase L, M, Q or H; omission means M. xDimensionMillimetres must be a JSON number from 0.396 through 0.990 in increments of 0.001; omission means 0.495. Its value must be exactly representable as a decimal; excess precision is rejected rather than rounded. Exponents and trailing zeroes are accepted when exact. Neither field accepts null. An empty barcode object uses both defaults. Keep the saved barcode object when changing unrelated product details.
Ajouter des liens et des languesProduct operations and history
All paths below are relative to /api/v1. Use the OpenAPI reference for complete schemas. Reads and temporary previews need a read-only key; product changes and retained artwork generation need a read/write key.
Single-product endpoints
| Request | What it does |
|---|---|
| GET /products | List your products, optionally filtering by GTIN. |
| GET /products/{id} | Read current configuration, publication state and removalOperationId. |
| POST /products | Create an unpublished product and its first version. |
| PUT /products/{id} | Replace configuration and create the next immutable version. |
| POST /products/{id}/custom-domain | Permanently attach one already-connected active custom domain using expectedVersionId and resolverUrl; publish the returned version separately. |
| GET /products/{id}/versions | Read retained versions, newest first. |
| POST /products/{id}/versions/{number}/restore | Copy historical content into a new current version while preserving any permanent custom-domain attachment; send expectedVersionId and publish separately. |
| GET /products/{id}/publications | Read publication attempts and their safe outcomes. |
| POST /products/{id}/publications | Publish exactly the version selected by expectedVersionId. |
| POST /products/{id}/disable | Start resolver removal; return 202 and a product Location to poll. |
| POST /products/{id}/disable/retry | Retry a failed removal using its expectedRemovalOperationId. |
| POST /products/{id}/enable | Enable a completed removal as unpublished, subject to plan capacity. |
| DELETE /products/{id} | Permanently delete a fully disabled product using the expected version and removal operation. |
| POST /products/{id}/barcode-preview | Render a temporary SVG for the expected current version. |
| POST /products/{id}/barcodes | Retain production SVG for the expected current published version. |
| GET /barcodes/{id}/svg | Download existing SVG artwork owned by your organisation. |
Products report unpublished, publishing, published, publishFailed, disabling, disabled or disableFailed. After disabling, poll GET /products/{id} until removal completes. Read removalOperationId from that product before enabling, retrying or deleting. Enable requires completed removal; retry requires failed removal; deletion requires the current version and completed removal. Re-enabling leaves the product unpublished. Existing plan limits and lifecycle cooldowns apply.
POST /api/v1/products/PRODUCT_ID/enable
{ "expectedRemovalOperationId": "REMOVAL_OPERATION_ID" }
POST /api/v1/products/PRODUCT_ID/disable/retry
{ "expectedRemovalOperationId": "REMOVAL_OPERATION_ID" }
DELETE /api/v1/products/PRODUCT_ID?expectedVersionId=CURRENT_VERSION_ID&expectedRemovalOperationId=REMOVAL_OPERATION_ID
Gérer vos produits
Product, version and publication lists return items and nextOffset. Use nextOffset for the next request until it is null. offset defaults to 0 and accepts 0–2147483546; limit defaults to 50 and accepts 1–100. Omit optional query parameters to use defaults; empty values are rejected. Pagination is a live view, so concurrent changes may shift results. Product lists accept gtin; compare resolverUrl as well when locating a particular printed identity.
GET /api/v1/products?gtin=09506000134352&offset=0&limit=50
Product and publication times come from the server. They use UTC with exactly six fractional digits and a trailing Z, for example 2026-11-03T14:25:30.123456Z. publishedAtUtc is null before a successful publication and completedAtUtc is null while an attempt is incomplete. A lifecycle error's nextDisableAtUtc uses the same format. V1 requests do not accept client-authored timestamps.
Publish and manage several products
Send up to five distinct products per batch. The same ownership, version and plan checks apply as for individual operations. After selection validation, items run in request order; the batch is not one transaction. Read every item result, even when the HTTP request succeeds. Disable and enable first check ownership of the complete selection: any missing or foreign product rejects the whole request with 404 before changes begin.
POST /api/v1/products/batch/publish
{
"items": [
{ "productId": "FIRST_PRODUCT_ID", "expectedVersionId": "FIRST_VERSION_ID" },
{ "productId": "SECOND_PRODUCT_ID", "expectedVersionId": "SECOND_VERSION_ID" }
]
}
{
"items": [
{ "productId": "FIRST_PRODUCT_ID", "status": 200, "code": null },
{ "productId": "SECOND_PRODUCT_ID", "status": 200, "code": null }
],
"stopped": false
}
Request fields for each batch action
- POST /products/batch/publish accepts items containing productId and expectedVersionId.
- POST /products/batch/disable accepts productIds.
- POST /products/batch/enable accepts items containing productId and expectedRemovalOperationId.
- POST /products/batch/delete accepts items containing productId, expectedVersionId and expectedRemovalOperationId.
- All four routes require a read/write key. Batch creation, replacement, restoration and artwork generation are not offered in v1.
Each returned item has productId, an HTTP status number and an optional safe code. Successful publication and enabling return 200, disabling returns 202 and deletion returns 204. Publication reports foreign products as item-level 404; deletion treats them as absent with 204 and changes nothing. Publication and deletion can return stopped: true with 503 for an uncertain item and 424 for later items not attempted. Disable or enable can instead return an infrastructure error after some items have succeeded. After any uncertain outcome or lost response, read product state before continuing.
Split larger selections into groups of at most five. If publishing times out, stopped can be true: the uncertain item returns 503 and later items return 424 because they were not attempted. Publication recovery may still finish the uncertain item. Read each submitted product and its publication history before retrying, including after a lost response.
Understand errors and retry safely
- 400: check the request fields and correct invalid input.
- 401: check that the secret is correct and the key has not expired or been revoked.
- 403: check the key's permission and the organisation's paid plan.
- 404: check that the resource belongs to the key's organisation. Missing and foreign resources have the same response; deleting either returns 204 without changing another organisation's data.
- 409: read the current product and review version, lifecycle or capacity conflicts before continuing.
- 413: reduce the request body to at most 256 KiB.
- 415: send UTF-8 application/json or application/*+json for an operation that requires a body.
- 429: wait for the Retry-After delay before making another request.
- 5xx: read product state before retrying; work may already have been accepted.
- Error responses use Problem Details with a safe code when available and traceId. Keep the X-Activity-Id response header when contacting support; never include your key.
The API does not support idempotency-key response replay. After an uncertain create, look up the GTIN and resolver before trying again. After any other uncertain write, read the current version and lifecycle state. Refresh after 409 and review the new state instead of blindly retrying. Infrastructure errors can occur after work was accepted; an HTTP failure does not prove that no change occurred.
The management API allows 120 requests per minute per organisation per API instance, shared by all its keys. A batch is bounded to five products. Requests above the rate limit return 429 and Retry-After; respect that delay. Request bodies cannot exceed 256 KiB. These management limits are separate from customer scans.
OpenAPI reference
The versioned customer API starts at /api/v1 on the API host. Its OpenAPI reference describes only the customer endpoints, authentication, request fields and responses. Import it into your API client or use it to generate an integration client. The reference is public; product requests require a key.
Download the OpenAPI JSON reference