Everything the dashboard does, you can do over HTTP. Base URL https://id.sunrise2d.com.
Send your API key as a bearer token. Find it under Settings.
curl https://id.sunrise2d.com/api/products \
-H "Authorization: Bearer YOUR_API_KEY"
API access is included on the Operations plan. The audit and QR endpoints are public and need no key.
Score a catalog. Public, rate limited to 10 requests per hour per IP.
curl -X POST https://id.sunrise2d.com/api/audit \
-H "Content-Type: application/json" \
-d '{
"email": "you@yourbrand.com",
"company": "Your Brand Co.",
"csv": "gtin,name,url\n036000291452,Hot Sauce,\n"
}'
Returns a readiness score (0–100), a letter grade, a summary, a prioritised checklist, and per-row findings. Header row must include a gtin or upc column; name and url are optional.
Retrieve a stored report. Shareable — no key required.
Packaging-ready SVG with physical dimensions in millimetres and a 4-module quiet zone.
| Parameter | Default | Notes |
|---|---|---|
module | 0.5 | Module size in mm. Minimum 0.396 for retail POS. |
ecc | M | L, M, Q, H. M is the packaging default — higher inflates the symbol. |
lot | — | AI (10) batch/lot |
serial | — | AI (21) serial number |
cpv | — | AI (22) consumer product variant |
curl "https://id.sunrise2d.com/api/qr/036000291452.svg?module=0.5&lot=LOT2027A" -o code.svg
Same symbol plus print metadata and conformance warnings — symbol width, module count, quiet zone, and whether the size is below the retail POS minimum.
Create or update a product and its resolver destinations.
curl -X PUT https://id.sunrise2d.com/api/products/036000291452 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Hot Sauce 12oz",
"attributes": { "17": "270630" },
"links": [
{ "linkType": "gs1:pip", "href": "https://yourbrand.com/hot-sauce", "default": true }
],
"approved": true
}'
approvedmust betruefor links to go live. Without it the change is staged, not published. This is deliberate: these routes back codes that may already be printed on packaging.
Full export of every product and route on your account, as JSON. This is the anti-lock-in guarantee — one call, no ticket, no waiting.
Register an endpoint and we POST signed JSON when things happen.
| Event | Fires when |
|---|---|
audit.completed | A readiness audit finishes |
product.published | A resolver route goes live |
destination.down | A destination fails checks 3× in 24h |
destination.recovered | It starts responding again |
conformance.regression | A previously conformant code stops validating |
Each request carries X-Sunrise2D-Timestamp and X-Sunrise2D-Signature. The signature is HMAC-SHA256 over {timestamp}.{raw body}, hex encoded, prefixed v1=.
import crypto from "node:crypto";
function verify(rawBody, headers, secret) {
const ts = headers["x-sunrise2d-timestamp"];
const sig = headers["x-sunrise2d-signature"].replace(/^v1=/, "");
// Reject anything older than 5 minutes to prevent replay.
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected = crypto.createHmac("sha256", secret)
.update(`${ts}.${rawBody}`).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
}
Retries use exponential backoff over roughly three days: 1 minute, 5 minutes, 25 minutes, 2 hours, 10 hours, 2 days. Return any 2xx to acknowledge. Delivery is at-least-once, so make your handler idempotent using the event id.
The resolver itself is public and standards-based — it is what point-of-sale systems and phones hit.
| Request | Returns |
|---|---|
/01/{gtin14} | 307 to the default destination |
/01/{gtin14}?linkType=gs1:pip | 307 to that specific link type |
/01/{gtin14}?linkType=all | Full linkset as JSON |
Accept: application/linkset+json | Same linkset, content-negotiated |
/01/{gtin14}/10/{lot} | Lot-specific route — used for recalls |
/.well-known/gs1resolver | Resolver self-description |
| Code | Meaning |
|---|---|
| 400 | Invalid input — the body explains which GTIN or AI value failed and why |
| 401 | Missing or invalid API key |
| 402 | Plan GTIN limit reached |
| 413 | Catalog too large for the free audit |
| 429 | Rate limited — see X-RateLimit-Reset |
Validation errors are specific by design. A bad check digit tells you the digit we expected, not just "invalid".