# CR80: HTTP workflow for card-design agents

CR80 is vendor-neutral card-design software. MCP is optional. An agent with
HTTPS/JSON tools can discover the format, obtain user-approved design access,
create a saved card, and render its preview without an MCP connection.
Purchases currently require the customer's CR80 browser sign-in and explicit
production and purchase approvals. Design authorization never authorizes buying
cards, configuring printers, or submitting local-printer jobs.

## Public discovery (no account)

- Design input JSON Schema: https://cr80.se/design-format.schema.json
- Downloadable example draft: https://cr80.se/design-example.json
- Available font names and attachment options: GET https://cr80.se/api/design-fonts
- Editor: https://cr80.se/en/editor
- Integration and rendering documentation: https://cr80.se/en/developers
- Printing partners: GET https://cr80.se/api/partner/list
- Partner catalogue: GET https://cr80.se/api/partner/catalog?id=PARTNER_ID
- CR80 direct-checkout catalogue: GET https://cr80.se/api/direct-checkout/catalog

GET the chosen catalogue each time. An enabled catalogue is not a production
availability guarantee. Physical SKUs, supported features and stock constrain
editing and ordering. Do not assume a particular vendor, SKU or price.
Tryckkort is one independent partner; its account and checkout are separate.
Partners with fulfillment=js use their own checkout. Never send a CR80 account
token to a printing partner or authenticate through the other product's domain.

## Generate a design

The example is a version 1 draft with no physical SKU selected. Edit its text and
layers, then select a destination and its supported base card before production.
All coordinates and dimensions are millimetres. Landscape trim is
85.6 × 53.98 mm with 1 mm bleed for artwork that reaches an edge.
Amounts returned by pricing are integer öre. Never calculate or promise a price
from a static document. The JSON Schema is structural: server validation,
font/image availability, data bindings and production preflight remain required.
NFC/chip cards cannot be hole-punched. Magnetic coercivity belongs to the SKU.

## User-approved HTTP design access

1. POST https://cr80.se/api/auth/device/code with Content-Type: application/json:
   {"client_id":"cr80-http","scope":"designs"}
2. Show the returned verification_uri_complete (or verification_uri and
   user_code) to the user. They sign in to CR80 and approve the integration.
   Never ask for their password or copied browser cookies.
3. Poll POST https://cr80.se/api/auth/device/token with:
   {"client_id":"cr80-http","device_code":"RETURNED_DEVICE_CODE",
    "grant_type":"urn:ietf:params:oauth:grant-type:device_code"}
   Respect the returned interval and expires_in. authorization_pending means
   wait; slow_down increases the interval by five seconds. Stop on access_denied
   or expired_token. Keep the resulting access_token private.
4. Use Authorization: Bearer ACCESS_TOKEN for the design endpoints below.
   This scope does not authorize partner orders, direct checkout or hardware.

## Create, edit and preview over HTTP

POST https://cr80.se/api/designs with Content-Type: application/json and:
{"requestId":"NEW_UUID_FOR_THIS_CREATION","design":COMPLETE_V1_DESIGN}

The response contains designId (numeric), projectId (UUID), revision, editorUrl,
saved and created. Open editorUrl for the owner to review the card. A new
creation returns 201; an identical retry returns 200. Reuse the requestId and
identical input after a timeout. Changed input with the same requestId returns
409 CREATION_CONFLICT. Organization projects additionally need an authorized
x-cr80-organization-id header and the organization's current processor agreement.

- GET https://cr80.se/api/design-projects/PROJECT_UUID returns the owned project.
- POST the same URL with {"action":"attach-font","revision":CURRENT_REVISION,
  "family":"Satisfy"} attaches a reviewed font and license from /api/design-fonts.
  No binary or URL argument is needed. Existing customer font files are preserved.
  Use the returned revision, keep the attached fonts[] when editing, and use the
  exact family name on regular text layers. Pinned families need no attachment.
  Never invent font bytes or assume system fonts exist. On an uncertain attachment
  result, reread the project before retrying; stale revisions return 409.
- PUT the same URL with {"revision":CURRENT_REVISION,"design":COMPLETE_V1_DESIGN}
  commits an edit. Read the current revision first; do not overwrite a conflict.
- POST the same URL with {"action":"preview","revision":CURRENT_REVISION,
  "side":"front"} renders the exact saved revision. For batch cards include
  rowIndex and preview both sides and every intended layout. The response has a
  base64 png and revision. Missing assets or stale revisions fail explicitly.
- POST the same URL with {"action":"catalog"} reads that project's catalogue.

A standalone draft can also be downloaded as .cr80.json and imported in the
editor without any machine authorization. JSON upload is not an order.

## Price and purchase: the customer's browser

For CR80 direct checkout, use the signed-in editor's existing checkout UI. The
following describes its HTTP contract for same-origin browser integrations;
it is not a public server-to-server order authorization flow. A cr80-http token
is rejected here, even when it has access to the design.

1. GET /api/direct-checkout/data-context returns the current privacy notice and
   available organizations. The customer selects personal_household or an
   authorized organization and reviews the current information in the browser.
2. POST /api/direct-checkout/data-context with kind, organizationId (only for an
   organization), privacyNotice {documentId,version,sha256}, and
   noticeAcknowledged:true. An organization needs its current processor
   agreement. The response contains a short-lived token. Do not set an
   acknowledgement without the customer's review.
3. POST /api/direct-checkout/quote with {design,rows,quantity}, the CR80 browser
   session, and x-cr80-data-context: TOKEN. Explicit baseCardSku, printMode and
   targetDpi are required. rows must match the quantity when personalized.
   The quote returns quoteId, retail, expiresAt, targetWorkingDays, termsVersion
   and productionDisclosure. Quotes expire after 15 minutes.
4. Show the price, productionDisclosure.text, and the current purchase terms
   from GET /api/direct-checkout/quotes/QUOTE_UUID/terms. Obtain explicit approval
   for the exact design, quantity, price, production and purchase terms.
5. POST /api/direct-checkout/orders with the identical design, rows and quantity,
   quoteId, customerEmail, shipping {name,street,postalCode,city,country,phone},
   billingSameAsShipping and termsAccepted:true. Optional billing uses the same
   address fields plus orgNumber/reference. Delivery is currently Sweden only.
   Include the browser session and headers x-cr80-data-context: TOKEN,
   x-cr80-quote-id: QUOTE_UUID and
   x-cr80-production-disclosure: productionDisclosure.sha256.

A new accepted order returns 201 with ref, statusUrl and productionPending.
An identical accepted retry returns 200. A quote is the order idempotency key:
retry with the same quoteId, headers and exact body after an uncertain response.
Never request a new quote just to retry an order that might already exist.
A design, catalogue or legal revision can invalidate a new order; obtain a new
quote and new customer approval when the original order was not accepted.
Treat statusUrl as a private capability. Do not publish or log it.

## Failures and limits

- 400: invalid request, design or scope; fix input rather than repeat it.
- 401/403: sign-in or ownership failure; never switch customer accounts silently.
- 409: conflicting creation, stale revision, changed quote or production choice.
- 413: request exceeds the route's body limit (checkout: 10 MiB).
- 422: design/preflight failure or incompatible production settings.
- 428: current data-processing context, agreement or production approval needed.
- 429: rate limit; back off. Checkout quote/orders allow 10 requests/minute/IP.
- 503: checkout, legal information or production offer unavailable; do not
  interpret an enabled catalogue or preview as an accepted order.

## Optional MCP

Remote server: https://cr80.se/mcp (Streamable HTTP with CR80 OAuth approval).
Use list_fonts and attach_font for the same server-managed font attachment flow.
Remote authorization uses authorization code with PKCE, distinct from the
HTTP device flow above. Existing integrations remain supported. No Tryckkort
session, MCP endpoint or account token is used to authenticate CR80.
