Encomende a partir do seu sistema

A XYZ Prints aceita encomendas dos sistemas dos parceiros — lojas, aplicações, fluxos de trabalho — através de uma pequena API HTTP: envie as imagens, orçamente a encomenda, faça-a e acompanhe-a até à porta do seu cliente.

Endereço base: https://www.xyz-prints.com/api/v1/partner

Em cinco passos

  1. Peça ao estúdio uma conta de parceiro: recebe uma chave e as suas condições — em conta ou pré-pagamento, um limite de crédito, um desconto.
  2. Envie cada imagem (POST /uploads) e guarde o id que recebe.
  3. Orçamente a encomenda (POST /quote): cada linha, as opções de envio, o IVA, o total — e se entraria logo em produção.
  4. Faça-a (POST /orders) com a sua referência e uma Idempotency-Key. É orçamentada de novo aqui; envie expected_total para que um preço que mudou seja recusado, nunca cobrado.
  5. Acompanhe-a no seu webhook, ou pergunte GET /orders?updated_since= de vez em quando.

Uma conta de parceiro, uma chave, uma pergunta: info@xyz-prints.com.

Autenticação

Cada pedido leva a sua chave de parceiro no cabeçalho Authorization: Bearer <chave>. A chave só abre esta API, e só as suas encomendas e ficheiros. Guarde-a no seu servidor — nunca num navegador ou numa aplicação. O estúdio cria e revoga chaves: peça uma nova se ela for exposta. Pode também restringir a sua chave aos endereços de onde os seus servidores chamam, para que uma chave exposta não sirva em mais lado nenhum.

curl \
  -H "Authorization: Bearer $XYZ_KEY" \
  https://www.xyz-prints.com/api/v1/partner

Os valores são em EUR com IVA incluído, como na loja; a taxa segue o país de entrega e o seu número de IVA, verificado no VIES. Os preços são sempre calculados aqui — o seu sistema nunca calcula valores.

Ficheiros

Envie cada imagem uma vez, como campo file de multipart/form-data. Formatos: tiff, jpg, png, pdf, psd, heic, bmp. Até 2048 MB por ficheiro; um pedido único leva até 8 MB, por isso envie o que for maior em partes. Um PDF dá um id por página. Um ficheiro não encomendado em 72 horas é apagado.

curl \
  -H "Authorization: Bearer $XYZ_KEY" \
  -F "file=@harbour-at-dusk.tif" \
  https://www.xyz-prints.com/api/v1/partner/uploads
201 — Resposta
{
  "files": [
    {
      "id": "k3Fq9w2LxP0aZt7c",
      "name": "harbour-at-dusk.tif",
      "bytes": 88213504,
      "width_px": 7200,
      "height_px": 9000,
      "dpi": 300,
      "dpi_assumed": false,
      "colour": {
        "mode": "RGB",
        "bit_depth": 16,
        "profile": true,
        "assigned": null
      },
      "expires_at": "2026-09-28T10:00:00.000Z"
    }
  ]
}

Em partes: corte o ficheiro em partes do mesmo tamanho (a última pode ser menor) e envie cada uma por POST em multipart com session (o seu id para o ficheiro: até 30 letras, algarismos, - e _), index (a partir de 0), total, chunk_size, size (o ficheiro inteiro, em bytes), name e chunk. Cada parte responde 202; a última responde 201 com o ficheiro.

A resposta dá os píxeis da imagem, a resolução que declara e o seu perfil de cor. A resolução na impressão depende do tamanho encomendado: o orçamento avisa abaixo de 100 DPI, o que o estúdio considera uma impressão razoável.

O que se pode encomendar

GET /catalogue lista os ids de hoje para cada tipo de linha. Abaixo, a mesma lista, lida agora.

curl \
  -H "Authorization: Bearer $XYZ_KEY" \
  https://www.xyz-prints.com/api/v1/partner/catalogue

Impressões fine-art kind: "print"

5682Hahnemühle German Etching 310grolo, até 110 × 1200 cm · 310 g/m² · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black
5683Hahnemühle Museum Etching 350grolo, até 110 × 1200 cm · 350 g/m² · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black
5684Hahnemühle PhotoRag 308grolo, até 110 × 1200 cm · 308 g/m² · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black
5698Hahnemühle PhotoRag Baryta 315grolo, até 110 × 1200 cm · 315 g/m² · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black
5685Hahnemühle William Turner 310grolo, até 110 × 1200 cm · 310 g/m² · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black
5686Canson Infinity Edition Etching Rag 310grolo, até 110 × 1200 cm · 310 g/m² · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black
5687Canson Infinity Platine Fibre Rag 310grolo, até 110 × 1200 cm · 310 g/m² · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black
5937Canson Infinity Baryta Photographique 310grolo, até 110 × 1200 cm · 310 g/m² · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black
5934Canson Infinity Rag Photographique II 310grolo, até 61 × 1200 cm · 310 g/m² · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black
5688Hahnemühle Photo Pearl 310grolo, até 110 × 1200 cm · 310 g/m² · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black
5689Hahnemühle Photo Luster 260grolo, até 110 × 1200 cm · 260 g/m² · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black
5871Canson Infinity PhotoGloss Premium RC 270grolo, até 61 × 1200 cm · 270 g/m² · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black
5690XYZ White Velvet 270grolo, até 110 × 1200 cm · 270 g/m² · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black
5691Hahnemühle Studio Enhanced 210grolo, até 110 × 1200 cm · 210 g/m² · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black
5692Hahnemühle Art Canvas Smooth 370grolo, até 110 × 1200 cm · 370 g/m²
5872Canson Museum ProCanvas WCrolo, até 110 × 1200 cm · 385 g/m²
5699Awagami Murakumo Kozo Select Natural 42grolo, até 91 × 1200 cm · 42 g/m² · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black
5694Awagami Kozo White 70grolo, até 110 × 1200 cm · 70 g/m² · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black
5702Transparent film 165µrolo, até 61 × 1200 cm · 100 g/m² · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black
5696Fedrigoni Arena White Smooth 350gfolhas 51 × 72 51 × 72, 72 × 102 cm 72 × 102 cm · 350 g/m² · frente e verso · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black
5697Fedrigoni Arena White Rough 300gfolhas 51 × 72 51 × 72, 72 × 102 cm 72 × 102 cm · 300 g/m² · frente e verso · montagens: pvc-4mm-white, pvc-5mm-white, dibond-3mm, museum-board-1-6mm, museum-board-2-2mm, pvc-5mm-black

Opções

mounts
pvc-4mm-white PVC 4mm white · pvc-5mm-white PVC 5mm white · dibond-3mm Dibond 3mm · museum-board-1-6mm Museum Board 1.6mm · museum-board-2-2mm Museum Board 2.2mm · pvc-5mm-black PVC 5mm black
subframes
wood-battens Wood battens · aluminium-rails Aluminium rails

Uma linha “print”

Todas as linhas têm kind e qty (1–999); estes são os restantes.

CampoTipoO que é
paper_id
obrigatório
integerO papel, pelo seu id no catálogo.
width_cm
obrigatório
numberLargura da impressão tal como fica pendurada, em cm. Num papel de folha cortada, um dos seus formatos (em qualquer sentido).
height_cm
obrigatório
numberAltura da impressão tal como fica pendurada, em cm.
file
obrigatório
stringA imagem: um id de upload de POST /uploads. Um PDF dá um id por página.
fit"fit" | "fill"
por omissão "fit"
fit = a imagem inteira, o maior possível, com papel à vista onde as proporções diferem. fill = a impressão coberta de ponta a ponta, a imagem recortada a partir do centro.
border_cmnumber
por omissão 0
Margem branca a toda a volta, em cm. Uma folha cortada mantém sempre pelo menos a sua margem não imprimível.
rotate0 | 90 | 180 | 270
por omissão 0
Rodar a imagem no sentido dos ponteiros do relógio antes de a colocar, em graus.
cropobjectManter parte da imagem: {"x", "y", "w", "h"} em frações 0–1 da largura e altura (após a orientação EXIF). Cortado antes de fit ou fill.
back_filestringFrente e verso, num papel de folha cortada que imprime as duas faces: o id de upload do verso. Mesmo fit, margem e rotação.
back_blankboolean
por omissão false
Frente e verso com o verso em branco (a última página ímpar de um documento).
mountstringMontar a impressão numa placa, pelo id (options.mounts). Não com frente e verso.
subframestringUma grade de suspensão atrás da montagem, pelo id (options.subframes). Requer mount.
subframe_pctnumber
por omissão 60
O tamanho da grade em percentagem da impressão (o catálogo dá o intervalo).
framestringEmoldurar a impressão, pelo id (options.frames). Não com frente e verso.
glazingstringO vidro da moldura, pelo id (options.glazings), ou "none". Por omissão: o primeiro oferecido.
mattstringUm passe-partout dentro da moldura, pelo id (options.matts).
matt_cmnumber
por omissão 5
A largura do passe-partout a toda a volta, em cm (0–20).
notestringUma palavra para o estúdio sobre esta impressão (até 500 caracteres).
Exemplo
{
  "kind": "print",
  "qty": 2,
  "paper_id": 101,
  "width_cm": 40,
  "height_cm": 50,
  "file": "upl_…",
  "fit": "fill",
  "border_cm": 2
}

Produtos kind: "product"

gift-card-50Gift card — €50€50.00
film-scan-35mmFilm scanning — 35 mm frame€6.00

Uma linha “product”

Todas as linhas têm kind e qty (1–999); estes são os restantes.

CampoTipoO que é
product
obrigatório
stringO artigo, pelo seu slug (ou SKU) no catálogo.
variantstringA opção, pelo seu id no catálogo — obrigatória quando o artigo tem opções.
notestringUma palavra para o estúdio sobre esta linha (até 500 caracteres).
Exemplo
{
  "kind": "product",
  "qty": 3,
  "product": "the-slug"
}

Orçamente

POST /quote aceita o corpo de uma encomenda, sem a referência, e não grava nada. Responde o preço de cada linha, as opções de envio, o IVA e o total, avisos sobre as imagens (resolução baixa, recorte) e would_be: o estado em que a encomenda começaria hoje, com as suas condições.

curl \
  -H "Authorization: Bearer $XYZ_KEY" \
  -H "Content-Type: application/json" \
  -d @order.json \
  https://www.xyz-prints.com/api/v1/partner/quote
order.json — Exemplo
{
  "lines": [
    {
      "kind": "print",
      "qty": 2,
      "paper_id": 101,
      "width_cm": 40,
      "height_cm": 50,
      "file": "k3Fq9w2LxP0aZt7c",
      "fit": "fill",
      "border_cm": 2
    }
  ],
  "delivery": {
    "mode": "ship"
  },
  "ship_to": {
    "first_name": "Ana",
    "last_name": "Silva",
    "address_1": "Rua das Flores 12",
    "city": "Porto",
    "postcode": "4050-262",
    "country": "PT",
    "phone": "+351 912 345 678"
  },
  "note": "Gift — no invoice in the parcel."
}
Resposta
{
  "quote": {
    "currency": "EUR",
    "lines": [
      {
        "kind": "print",
        "name": "Hahnemühle Photo Rag 308",
        "qty": 2,
        "unit_price": 54.12,
        "total": 108.24,
        "vat_rate": 23,
        "details": [
          {
            "name": "File",
            "value": "harbour-at-dusk.tif"
          },
          {
            "name": "Sheet",
            "value": "40 × 50 cm"
          },
          {
            "name": "Image",
            "value": "36 × 46 cm"
          }
        ]
      }
    ],
    "warnings": [
      {
        "line": 0,
        "code": "cropped",
        "message": "fill cuts 3.2% of the picture away to cover the print; send fit to keep all of it."
      }
    ],
    "delivery": {
      "mode": "ship",
      "service": "ctt_express",
      "label": "CTT Expresso",
      "options": [
        {
          "id": "ctt_express",
          "label": "CTT Expresso",
          "price": 12.05,
          "days": [
            1,
            2
          ]
        }
      ]
    },
    "totals": {
      "goods": 108.24,
      "discount": 0,
      "shipping": 12.05,
      "vat": 22.49,
      "total": 120.29
    },
    "vat": {
      "rate": 23,
      "exempt": false,
      "reason": null,
      "number": "PT509999999",
      "status": "valid_domestic"
    },
    "would_be": {
      "state": "in_production",
      "holds": []
    }
  }
}

Encomende

POST /orders exige o cabeçalho Idempotency-Key: repetir com a mesma chave devolve o primeiro resultado e nunca cria uma segunda encomenda. A sua referência também é única — uma segunda encomenda com ela é recusada e a primeira vem na resposta. Envie expected_total, o total que o /quote lhe deu, e um preço que entretanto mudou é recusado (409 price_changed) em vez de cobrado.

curl \
  -H "Authorization: Bearer $XYZ_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: SHOP-10482-1" \
  -d @order.json \
  https://www.xyz-prints.com/api/v1/partner/orders
order.json — Exemplo
{
  "reference": "SHOP-10482",
  "lines": [
    {
      "kind": "print",
      "qty": 2,
      "paper_id": 101,
      "width_cm": 40,
      "height_cm": 50,
      "file": "k3Fq9w2LxP0aZt7c",
      "fit": "fill",
      "border_cm": 2
    }
  ],
  "delivery": {
    "mode": "ship"
  },
  "ship_to": {
    "first_name": "Ana",
    "last_name": "Silva",
    "address_1": "Rua das Flores 12",
    "city": "Porto",
    "postcode": "4050-262",
    "country": "PT",
    "phone": "+351 912 345 678"
  },
  "note": "Gift — no invoice in the parcel.",
  "expected_total": 120.29
}
201 — Resposta
{
  "order": {
    "number": 6214,
    "reference": "SHOP-10482",
    "state": "in_production",
    "holds": [],
    "status": "processing",
    "created_at": "2026-09-25T10:04:11.000Z",
    "updated_at": "2026-09-25T10:04:11.000Z",
    "currency": "EUR",
    "payment": {
      "terms": "account",
      "state": "unpaid",
      "paid_at": null
    },
    "totals": {
      "goods": 108.24,
      "discount": 0,
      "shipping": 12.05,
      "vat": 22.49,
      "total": 120.29,
      "refunded": 0
    },
    "vat": {
      "rate": 23,
      "exempt": false,
      "reason": null,
      "number": "PT509999999"
    },
    "delivery": {
      "mode": "ship",
      "service": "CTT Expresso",
      "ship_to": {
        "first_name": "Ana",
        "last_name": "Silva",
        "address_1": "Rua das Flores 12",
        "city": "Porto",
        "postcode": "4050-262",
        "country": "PT",
        "phone": "+351 912 345 678"
      },
      "tracking": null
    },
    "invoice": null,
    "note": "Gift — no invoice in the parcel.",
    "lines": [
      {
        "kind": "print",
        "name": "Hahnemühle Photo Rag 308",
        "qty": 2,
        "unit_price": 54.12,
        "total": 108.24,
        "vat_rate": 23,
        "details": [
          {
            "name": "File",
            "value": "harbour-at-dusk.tif"
          },
          {
            "name": "Sheet",
            "value": "40 × 50 cm"
          },
          {
            "name": "Image",
            "value": "36 × 46 cm"
          }
        ],
        "file": "k3Fq9w2LxP0aZt7c"
      }
    ]
  },
  "warnings": [
    {
      "line": 0,
      "code": "cropped",
      "message": "fill cuts 3.2% of the picture away to cover the print; send fit to keep all of it."
    }
  ]
}

ship_to é o destinatário: first_name e last_name, ou company; address_1, city, postcode e country (duas letras); telefone e e-mail ajudam a transportadora. A fatura é emitida à sua empresa — o estúdio guarda-a na sua conta. delivery.service escolhe uma das opções de envio do /quote; sem ela, a primeira. O destinatário não recebe nada do estúdio, a não ser que o peça.

Pagamento e aceitação

Em conta: uma encomenda que passa todas as verificações entra logo em produção. É faturada a si e paga por transferência contra a fatura; o estúdio regista o pagamento.

Pré-pagamento: a encomenda espera pelo valor. payment.pay_url é a sua página de pagamento (cartão, MB WAY, PayPal…); paga, entra em produção sozinha.

Uma retenção mantém a encomenda fora de produção até o estúdio a libertar; holds diz porquê:

  • review — A sua conta está configurada para uma pessoa ver cada encomenda primeiro.
  • credit_limit — Esta encomenda leva o saldo em conta além do seu limite de crédito.
  • no_credit_limit — A sua conta é a crédito, mas ainda não foi acordado um limite de crédito — o estúdio define-o e depois liberta a encomenda.

O estado de uma encomenda

EstadoSignificado
awaiting_paymentPré-pagamento: à espera do valor através de `payment.pay_url`. Paga, entra em produção sozinha.
heldFora de produção até o estúdio a libertar — ver `holds`.
in_productionAceite: a ser impressa, acabada e embalada.
shippedEntregue à transportadora; `delivery.tracking` quando há número.
completedEntregue ou levantada, e encerrada.
cancelledCancelada; nada mais acontece.
refundedReembolsada na totalidade.
failedUm pagamento falhou e a encomenda parou.

Uma encomenda retida, ou ainda à espera do pagamento, pode ser cancelada: POST /orders/{number}/cancel. Já em produção, escreva ao estúdio.

Acompanhe

GET /orders/{number} lê uma encomenda; GET /orders?reference= encontra a sua pela sua referência. Para manter o seu sistema a par sem webhook, pergunte GET /orders?updated_since=<o último updated_at que viu>: todas as encomendas alteradas desde então, da alteração mais antiga.

curl \
  -H "Authorization: Bearer $XYZ_KEY" \
  "https://www.xyz-prints.com/api/v1/partner/orders?updated_since=2026-09-25T10:04:11.000Z"

Webhooks

Dê ao estúdio um endereço https e cada alteração às suas encomendas é enviada por POST para lá, em JSON, com a encomenda tal como está. Responda com um 2xx em 8 segundos. Cada mensagem é enviada uma vez: se esteve em baixo, GET /orders?updated_since= põe-no a par. As mensagens podem chegar fora de ordem — compare data.order.updated_at.

EventoQuando
order.createdA encomenda foi feita — `order.state` diz se entrou em produção, ficou retida ou aguarda pagamento.
order.acceptedUma encomenda retida ou pré-paga entrou em produção.
order.paidO pagamento foi registado.
order.shippedSaiu do estúdio; `tracking` quando há número.
order.readyPronta a levantar no estúdio.
order.invoicedA fatura foi emitida; `invoice` é o seu número.
order.completedEncerrada.
order.cancelledCancelada.
order.refundedReembolsada.
order.updatedOutra alteração (`from` e `to` para o estado).

O «Enviar um teste» do estúdio envia o evento ping, assinado da mesma forma, sem encomenda.

order.shipped — Exemplo
{
  "id": "evt_6f1c0a9b2d3e4f5a6b7c8d9e",
  "event": "order.shipped",
  "created_at": "2026-09-27T15:31:02.000Z",
  "data": {
    "tracking": "EA123456789PT",
    "order": {
      "number": 6214,
      "reference": "SHOP-10482",
      "state": "shipped",
      "holds": [],
      "status": "processing",
      "created_at": "2026-09-25T10:04:11.000Z",
      "updated_at": "2026-09-25T10:04:11.000Z",
      "currency": "EUR",
      "payment": {
        "terms": "account",
        "state": "unpaid",
        "paid_at": null
      },
      "totals": {
        "goods": 108.24,
        "discount": 0,
        "shipping": 12.05,
        "vat": 22.49,
        "total": 120.29,
        "refunded": 0
      },
      "vat": {
        "rate": 23,
        "exempt": false,
        "reason": null,
        "number": "PT509999999"
      },
      "delivery": {
        "mode": "ship",
        "service": "CTT Expresso",
        "ship_to": {
          "first_name": "Ana",
          "last_name": "Silva",
          "address_1": "Rua das Flores 12",
          "city": "Porto",
          "postcode": "4050-262",
          "country": "PT",
          "phone": "+351 912 345 678"
        },
        "tracking": "EA123456789PT"
      },
      "invoice": null,
      "note": "Gift — no invoice in the parcel.",
      "lines": [
        {
          "kind": "print",
          "name": "Hahnemühle Photo Rag 308",
          "qty": 2,
          "unit_price": 54.12,
          "total": 108.24,
          "vat_rate": 23,
          "details": [
            {
              "name": "File",
              "value": "harbour-at-dusk.tif"
            },
            {
              "name": "Sheet",
              "value": "40 × 50 cm"
            },
            {
              "name": "Image",
              "value": "36 × 46 cm"
            }
          ],
          "file": "k3Fq9w2LxP0aZt7c"
        }
      ]
    }
  }
}

Cada mensagem é assinada: X-WebOffice-Signature: t=<tempo unix>,v1=<HMAC-SHA256 de «t.corpo» com o seu segredo de assinatura, em hexadecimal>. Verifique-a contra o corpo tal como chegou, e recuse um t a mais de cinco minutos. O estúdio dá-lhe o segredo e pode renová-lo.

import crypto from 'node:crypto'

// raw: the request body exactly as it arrived (a string, before any JSON parsing)
// header: the X-WebOffice-Signature header; secret: your signing secret (whsec_…)
export function verified(raw, header, secret) {
  const parts = Object.fromEntries(String(header).split(',').map((kv) => kv.trim().split('=')))
  const t = Number(parts.t)
  if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > 300) return false
  const want = crypto.createHmac('sha256', secret).update(t + '.' + raw).digest('hex')
  const got = String(parts.v1 || '')
  return got.length === want.length && crypto.timingSafeEqual(Buffer.from(got), Buffer.from(want))
}

Erros

Um erro responde um estado HTTP e { error: { code, message, fields } }; fields indica cada problema como o corpo o escreve (lines[0].width_cm). Os códigos são estáveis, as mensagens são para pessoas.

422 — Exemplo
{
  "error": {
    "code": "invalid_lines",
    "message": "Hahnemühle Photo Rag 308: That size is too large for this paper — the largest print it can make is 111.8 × 1500 cm.",
    "fields": [
      {
        "field": "lines[0].width_cm",
        "code": "size_not_available",
        "message": "Hahnemühle Photo Rag 308: That size is too large for this paper — the largest print it can make is 111.8 × 1500 cm."
      }
    ]
  }
}
CódigoSignificado
unauthorisedSem chave, ou não é uma chave de parceiro (401).
partner_inactiveO parceiro da chave está desativado, ou a marca deixou de aceitar encomendas de parceiros (403).
address_not_allowedA sua conta só aceita pedidos dos endereços que o estúdio indicou, e este não é um deles (403).
rate_limitedAcima de um limite — pedidos, linhas orçamentadas, encomendas por hora, ou chaves erradas de um endereço: aguarde os segundos de Retry-After (429).
internalAlgo falhou do nosso lado; nada do que enviou estava errado. Tente de novo (500).
storage_fullDemasiados ficheiros seus à espera de encomenda: encomende-os ou deixe-os expirar primeiro (507).
unknown_callPedido inexistente (404).
invalid_queryUm parâmetro de consulta não é o que o pedido aceita — updated_since é uma data-hora ISO 8601 (400).
invalid_bodyO corpo não é o que o pedido aceita; `fields` indica cada problema (422).
invalid_linesUma linha não pode ser feita como pedida — papel desconhecido, tamanho impossível, acabamento não oferecido, ficheiro que não é seu; `fields` indica a linha e o campo (422).
no_shippingNenhuma transportadora leva esta encomenda a esta morada, ou o seu transporte é orçamentado à mão (422).
unknown_servicedelivery.service não é uma das opções para esta encomenda (422).
destination_refusedO estúdio não envia para esse país (422).
pickup_not_offeredO levantamento no estúdio não está disponível (422).
idempotency_key_requiredPOST /orders exige o cabeçalho Idempotency-Key (400).
in_progressOutro pedido com esta Idempotency-Key ainda está a correr (409).
duplicate_referenceJá existe uma encomenda com esta referência; vem em `order` (409).
price_changedO total não é o seu expected_total; o `total` atual vem na resposta — orçamente de novo (409).
pausedO estúdio não aceita encomendas até `until` (503).
unknown_orderNenhuma encomenda sua com esse número (404).
not_cancellableA encomenda já está em produção, enviada ou concluída (409).
unknown_fileNenhum upload seu com esse id — expirou, ou nunca foi enviado (404).
no_fileO upload não trazia ficheiro (400).
format_not_acceptedEsse tipo de ficheiro não é aceite (415).
too_largeUm ficheiro, um pedido de upload único ou o corpo de uma encomenda é grande demais: envie o ficheiro em partes, ou menos linhas (413).
unreadableO ficheiro não pôde ser lido como imagem ou PDF (415).
upload_failedUma parte não correspondia ao upload a que dizia pertencer (400).

Por chave, a cada dez minutos: 600 pedidos, 2000 linhas orçamentadas (um orçamento ou uma encomenda conta as suas linhas) e 8192 MB de uploads. Por conta: 120 encomendas por hora, 20 GB de ficheiros à espera de encomenda e um corpo até 1024 KB. Acima de um limite a resposta é 429 com Retry-After (507 para os ficheiros). Pedidos com uma chave errada são limitados por endereço.

Todos os pedidos

GET /partner

A quem pertence a chave: as suas condições, o saldo em conta, os limites.

GET /partner/catalogue

O que se pode encomendar hoje, tipo a tipo: os ids que uma linha indica.

POST /partner/uploads

Enviar um ficheiro. Até ao limite de um pedido como campo multipart `file`; ficheiros maiores em partes. Um PDF dá um ficheiro por página.

no_file format_not_accepted too_large unreadable upload_failed storage_full unauthorised partner_inactive address_not_allowed rate_limited unknown_call internal

GET /partner/uploads/{id}

Um ficheiro enviado: píxeis, resolução e cor, e até quando fica guardado.

unknown_file unauthorised partner_inactive address_not_allowed rate_limited unknown_call internal

POST /partner/quote

Orçamentar uma encomenda sem a fazer: linhas, opções de envio, IVA, total — e se entraria logo em produção.

too_large invalid_body invalid_lines no_shipping unknown_service destination_refused pickup_not_offered unauthorised partner_inactive address_not_allowed rate_limited unknown_call internal

POST /partner/orders

Fazer uma encomenda. Orçamentada aqui, depois aceite em produção, retida, ou à espera do pagamento — conforme as suas condições.

  • Idempotency-Key (obrigatório) — A sua chave única para esta tentativa (8–200 caracteres). Repetir com a mesma chave devolve a primeira resposta e nunca cria uma segunda encomenda.

idempotency_key_required in_progress too_large invalid_body invalid_lines duplicate_reference price_changed no_shipping unknown_service destination_refused pickup_not_offered paused unauthorised partner_inactive address_not_allowed rate_limited unknown_call internal

GET /partner/orders

As suas encomendas, das mais recentes — ou todas as alteradas desde um momento, da alteração mais antiga, para manter o seu sistema a par.

  • ?updated_since= — Data-hora ISO 8601: encomendas alteradas depois dela, da mais antiga. Guarde o último updated_at e pergunte de novo.
  • ?reference= — A sua referência: a encomenda que a tem.
  • ?limit= — 1–100, por omissão 50. `more: true` indica outra página.

invalid_query unauthorised partner_inactive address_not_allowed rate_limited unknown_call internal

GET /partner/orders/{number}

Uma das suas encomendas pelo número.

unknown_order unauthorised partner_inactive address_not_allowed rate_limited unknown_call internal

POST /partner/orders/{number}/cancel

Cancelar uma encomenda retida ou ainda à espera do pagamento. Já em produção, escreva ao estúdio.

unknown_order not_cancellable unauthorised partner_inactive address_not_allowed rate_limited unknown_call internal

GET /partner/openapi.json

Esta descrição em OpenAPI 3.1, para o seu gerador de código. Junte ?brand=<slug> para os campos de linha de uma marca.

O mesmo, em OpenAPI 3.1, para um gerador de código: https://www.xyz-prints.com/api/v1/partner/openapi.json?brand=xyz-prints