openapi: 3.0.3 info: title: Remo — API de certificados de autenticidade version: 1.0.0 description: | Emita certificados a cada venda e permita que qualquer pessoa os verifique, com prova em blockchain. Autenticação por `Authorization: Bearer ` (chave de API `rk_live_…` ou JWT do painel). servers: - url: https://SEU-DOMINIO tags: - name: Certificados - name: Públicas - name: Webhooks security: - apiKey: [] paths: /api/certificates: post: tags: [Certificados] summary: Emitir certificado description: Requer escopo `certificates:write`. Também aceita credenciais de loja parceira (headers x-retailer-code, x-retailer-key, x-brand-slug). requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/IssueRequest' } responses: '201': { description: Emitido, content: { application/json: { schema: { $ref: '#/components/schemas/Certificate' } } } } '400': { $ref: '#/components/responses/Error' } '401': { $ref: '#/components/responses/Error' } '403': { $ref: '#/components/responses/Error' } get: tags: [Certificados] summary: Listar certificados da marca description: Requer escopo `certificates:read`. parameters: - { name: q, in: query, schema: { type: string, maxLength: 100 } } - { name: status, in: query, schema: { type: string, enum: [active, revoked] } } - { name: page, in: query, schema: { type: integer, minimum: 1, default: 1 } } - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 25 } } responses: '200': description: Página de certificados content: application/json: schema: type: object properties: data: { type: array, items: { $ref: '#/components/schemas/Certificate' } } page: { type: integer } limit: { type: integer } total: { type: integer } /api/certificates/{code}: get: tags: [Públicas] summary: Consultar certificado (público) security: [] parameters: - { name: code, in: path, required: true, schema: { type: string, example: RM-7F2K9CX1M4QA } } responses: '200': { description: Visão pública, content: { application/json: { schema: { $ref: '#/components/schemas/PublicCertificate' } } } } '404': { $ref: '#/components/responses/Error' } '429': { $ref: '#/components/responses/Error' } /api/certificates/{code}/proof: get: tags: [Públicas] summary: Dados para conferência independente na blockchain security: [] parameters: - { name: code, in: path, required: true, schema: { type: string } } responses: '200': { description: Prova on-chain } '404': { $ref: '#/components/responses/Error' } /api/certificates/{code}/qr.svg: get: tags: [Públicas] summary: QR Code (SVG) que aponta para /v/{code} security: [] parameters: - { name: code, in: path, required: true, schema: { type: string } } responses: '200': { description: SVG, content: { image/svg+xml: { schema: { type: string } } } } '404': { $ref: '#/components/responses/Error' } /api/certificates/{code}/revoke: post: tags: [Certificados] summary: Revogar certificado description: Requer escopo `certificates:write`. Registra a revogação na blockchain. parameters: - { name: code, in: path, required: true, schema: { type: string } } requestBody: content: application/json: schema: { type: object, properties: { reason: { type: string, maxLength: 200 } } } responses: '200': { description: Revogado, content: { application/json: { schema: { $ref: '#/components/schemas/Certificate' } } } } '404': { $ref: '#/components/responses/Error' } '409': { $ref: '#/components/responses/Error' } /api/certificates/{code}/transfer/request: post: tags: [Públicas] summary: Pedir código de transferência (enviado por e-mail ao dono) description: Resposta idêntica exista ou não o e-mail (não revela o proprietário). security: [] parameters: - { name: code, in: path, required: true, schema: { type: string } } requestBody: required: true content: { application/json: { schema: { type: object, required: [email], properties: { email: { type: string, format: email } } } } } responses: '200': { description: Mensagem genérica } /api/certificates/{code}/transfer/confirm: post: tags: [Públicas] summary: Confirmar transferência de titularidade security: [] parameters: - { name: code, in: path, required: true, schema: { type: string } } requestBody: required: true content: application/json: schema: type: object required: [token, newName, newEmail] properties: token: { type: string } newName: { type: string, maxLength: 120 } newEmail: { type: string, format: email } responses: '200': { description: Certificado atualizado (visão pública), content: { application/json: { schema: { $ref: '#/components/schemas/PublicCertificate' } } } } '400': { $ref: '#/components/responses/Error' } '429': { $ref: '#/components/responses/Error' } /api/erp/webhook/{webhookId}: post: tags: [Webhooks] summary: Webhook de venda (ERP/loja) description: | Autenticação por `x-api-key` (ou `?key=`) no payload padrão; por HMAC-SHA256 do corpo para Shopify (`X-Shopify-Hmac-Sha256`) e WooCommerce (`X-WC-Webhook-Signature`). Idempotente por pedido. security: [] parameters: - { name: webhookId, in: path, required: true, schema: { type: string } } requestBody: content: application/json: schema: { $ref: '#/components/schemas/StandardOrder' } responses: '201': { description: Certificado(s) emitido(s) } '200': { description: Pedido já processado ou evento ignorado } '401': { $ref: '#/components/responses/Error' } /api/public/brands/{slug}/requests: post: tags: [Públicas] summary: Loja pede emissão à marca (fica pendente até aprovação) security: [] parameters: - { name: slug, in: path, required: true, schema: { type: string } } requestBody: required: true content: application/json: schema: type: object required: [storeName, contactEmail, nf, sku, ownerName, ownerEmail] properties: storeCode: { type: string } storeName: { type: string } contactEmail: { type: string, format: email } nf: { type: string } sku: { type: string } desc: { type: string } ownerName: { type: string } ownerEmail: { type: string, format: email } responses: '201': { description: Solicitação registrada } components: securitySchemes: apiKey: type: http scheme: bearer description: Chave de API `rk_live_…` (painel → API e chaves) ou JWT do painel. responses: Error: description: Erro content: application/json: schema: { type: object, properties: { error: { type: string } } } schemas: IssueRequest: type: object required: [nf, sku, ownerName, ownerEmail] properties: nf: { type: string, maxLength: 60 } sku: { type: string, maxLength: 60 } desc: { type: string, maxLength: 200 } ownerName: { type: string, maxLength: 120 } ownerEmail: { type: string, format: email } accent: { type: string, pattern: '^#[0-9a-fA-F]{6}$' } Certificate: type: object properties: code: { type: string } status: { type: string, enum: [active, revoked] } brand: { type: string } nf: { type: string } sku: { type: string } desc: { type: string } ownerName: { type: string } ownerEmail: { type: string } issuedAt: { type: string, format: date-time } issuedVia: { type: object } tokenId: { type: string } dataHash: { type: string } txHash: { type: string } txUrl: { type: string, nullable: true } verifyUrl: { type: string } PublicCertificate: type: object properties: code: { type: string } status: { type: string, enum: [authentic, revoked, tampered] } brand: { type: object, properties: { name: { type: string }, slug: { type: string } } } product: { type: object, properties: { sku: { type: string }, description: { type: string } } } ownerName: { type: string, description: Nome abreviado; e-mail nunca é exposto } history: { type: array, items: { type: object } } verification: { type: object } proof: { type: object } StandardOrder: type: object required: [nf, owner_name, owner_email] properties: nf: { type: string } order_id: { type: string, description: Chave de idempotência } owner_name: { type: string } owner_email: { type: string, format: email } items: type: array items: type: object required: [sku] properties: sku: { type: string } desc: { type: string } quantity: { type: integer, minimum: 1, maximum: 50 }