{
  "openapi": "3.1.0",
  "info": {
    "title": "API de integração do RegistrarObra",
    "version": "2026-09-19",
    "summary": "Registro de anterioridade de obras por integração, sob contrato.",
    "description": "O RegistrarObra comprova a anterioridade de um arquivo: que ele já existia numa data,\ncom âncora na blockchain Bitcoin.\n\n**O arquivo nunca sai da máquina do cliente.** A API recebe a impressão digital SHA-256\ndo arquivo (64 caracteres hexadecimais minúsculos), calculada do lado do integrador,\nalém do nome, do tamanho e dos dados da obra e da autoria declarada. Nunca o arquivo.\n\nO acesso é concedido pela equipe do RegistrarObra, sob contrato. A documentação é\npública; o acesso não é. Contato comercial: contato@registrarobra.com.br.\n\nLimites do serviço: não é registro público e não substitui INPI, Biblioteca Nacional,\nANCINE ou cartório; prova anterioridade, não autoria; a autoria é declarada pelo\ntitular e não é verificada. O certificado costuma sair em até 15 minutos depois que o\nregistro entra em processamento; a confirmação da âncora na blockchain Bitcoin leva\nalgumas horas.\n\nDocumentação completa: https://registrarobra.com.br/desenvolvedores\n",
    "contact": {
      "name": "Comercial RegistrarObra",
      "email": "contato@registrarobra.com.br",
      "url": "https://registrarobra.com.br/desenvolvedores#acesso"
    },
    "license": {
      "name": "Proprietária — uso sob contrato",
      "url": "https://registrarobra.com.br/termos"
    }
  },
  "servers": [
    {
      "url": "https://auth.registrarobra.com.br/functions/v1",
      "description": "Produção"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Conexão",
      "description": "Teste de conexão e saldo de créditos."
    },
    {
      "name": "Registro em lote",
      "description": "Registro de 1 a 25 obras por chamada; consome crédito."
    },
    {
      "name": "Consulta",
      "description": "Situação de lotes e registros, documentos e verificação pública."
    },
    {
      "name": "Descontinuados",
      "description": "Endpoints antigos que respondem 410."
    }
  ],
  "paths": {
    "/api-health-check": {
      "get": {
        "operationId": "healthCheck",
        "tags": [
          "Conexão"
        ],
        "summary": "Testa a conexão e mostra o saldo de créditos",
        "description": "Chamada sem efeito colateral: confere token, escopo e webhook, e devolve o saldo\nde créditos do titular da integração. Escopo `health:read`.\n",
        "x-required-scope": "health:read",
        "responses": {
          "200": {
            "description": "Conexão válida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthCheckResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api-submit-bulk-registration-job": {
      "post": {
        "operationId": "submitBulkRegistrationJob",
        "tags": [
          "Registro em lote"
        ],
        "summary": "Cria um lote de 1 a 25 registros",
        "description": "Registra de 1 a 25 obras com a mesma categoria e a mesma autoria. Cada item leva a\nimpressão digital SHA-256 do arquivo, o nome e o tamanho — nunca o arquivo.\n\nO lote é aceito (202) e processado em segundo plano: cada item vira um registro e\nconsome 1 crédito da conta titular. Item cujo arquivo já tinha registro fica como\n`duplicate` e não consome crédito. Escopo `bulk:create`.\n\nIdempotência: envie `external_job_id`. O mesmo valor nesta integração devolve o\nlote já criado (200, `idempotent: true`) em vez de criar outro.\n",
        "x-required-scope": "bulk:create",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkJobRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Lote já existente com o mesmo `external_job_id` (resposta idempotente).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkJobIdempotent"
                }
              }
            }
          },
          "202": {
            "description": "Lote aceito e na fila.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkJobAccepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Perfil do titular da integração não encontrado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Uma ou mais impressões digitais já têm registro (`details.file_hashes`), ou a conta\nnão tem créditos para o lote inteiro (`details.required_credits`,\n`details.available_credits`). Nada é criado.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "duplicado": {
                    "value": {
                      "error": "Uma ou mais hashes deste lote ja foram registradas anteriormente",
                      "details": {
                        "file_hashes": [
                          "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
                        ]
                      }
                    }
                  },
                  "semCredito": {
                    "value": {
                      "error": "Creditos insuficientes para este lote",
                      "details": {
                        "required_credits": 3,
                        "available_credits": 1
                      }
                    }
                  }
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api-get-bulk-job": {
      "get": {
        "operationId": "getBulkJob",
        "tags": [
          "Consulta"
        ],
        "summary": "Situação de um lote e dos itens",
        "description": "Informe `job_id` ou `external_job_id`. Só lotes criados por esta integração. Escopo `bulk:read`.",
        "x-required-scope": "bulk:read",
        "parameters": [
          {
            "name": "job_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "external_job_id",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/ExternalId"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Lote e itens, na ordem de envio.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkJobDetails"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api-get-registration": {
      "get": {
        "operationId": "getRegistration",
        "tags": [
          "Consulta"
        ],
        "summary": "Um registro e os documentos disponíveis",
        "description": "Registro da conta (ou do workspace) da integração. Quando `status` é `concluido`, o\ncertificado e a ficha podem ser baixados por `/api-get-registration-artifact`.\nEscopo `registration:read`.\n",
        "x-required-scope": "registration:read",
        "parameters": [
          {
            "name": "registration_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Registro encontrado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegistrationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api-list-registrations": {
      "get": {
        "operationId": "listRegistrations",
        "tags": [
          "Consulta"
        ],
        "summary": "Lista os registros mais recentes",
        "description": "Registros da conta (ou do workspace) da integração, do mais novo para o mais antigo.\nPaginação por cursor: quando houver mais, a resposta traz `next_before`; mande-o em\n`before` na chamada seguinte. Escopo `registration:read`.\n",
        "x-required-scope": "registration:read",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "name": "before",
            "in": "query",
            "description": "Data ISO 8601; devolve registros criados antes dela.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Página de registros.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegistrationList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api-get-registration-artifact": {
      "get": {
        "operationId": "getRegistrationArtifact",
        "tags": [
          "Consulta"
        ],
        "summary": "Link de download do certificado ou da ficha",
        "description": "Devolve uma URL assinada, válida por 120 segundos, para baixar o documento de um\nregistro concluído. Com `return=redirect`, responde 302 direto para a URL.\nEscopo `artifact:read`.\n",
        "x-required-scope": "artifact:read",
        "parameters": [
          {
            "name": "registration_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "artifact",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "certificate",
                "ficha"
              ]
            },
            "description": "`certificate`: certificado em PDF. `ficha`: ficha de cadastro em PDF."
          },
          {
            "name": "return",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "redirect"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "URL assinada de curta duração.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtifactResponse"
                }
              }
            }
          },
          "302": {
            "description": "Redirecionamento para a URL assinada (com `return=redirect`).",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "O registro ainda não foi concluído; os documentos saem depois da emissão.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api-verify-hash": {
      "get": {
        "operationId": "verifyHash",
        "tags": [
          "Consulta"
        ],
        "summary": "Verifica se uma impressão digital tem registro concluído",
        "description": "A mesma verificação pública da página https://registrarobra.com.br/verificar: diz se\nexiste registro concluído com esta impressão digital (do arquivo ou da ficha de\ncadastro) e, se existir, data, obra, autoria declarada e situação da âncora.\nNão olha a conta: o resultado é o que qualquer pessoa vê na página pública.\nEscopo `registration:read`.\n",
        "x-required-scope": "registration:read",
        "parameters": [
          {
            "name": "file_hash",
            "in": "query",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/Sha256Hex"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Resultado da verificação.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerifyResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api-create-upload-session": {
      "post": {
        "operationId": "createUploadSessionDeprecated",
        "tags": [
          "Descontinuados"
        ],
        "deprecated": true,
        "summary": "Descontinuado — o arquivo nunca é enviado",
        "security": [],
        "responses": {
          "410": {
            "$ref": "#/components/responses/Gone"
          }
        }
      }
    },
    "/api-get-upload-session": {
      "get": {
        "operationId": "getUploadSessionDeprecated",
        "tags": [
          "Descontinuados"
        ],
        "deprecated": true,
        "summary": "Descontinuado — o arquivo nunca é enviado",
        "security": [],
        "responses": {
          "410": {
            "$ref": "#/components/responses/Gone"
          }
        }
      }
    }
  },
  "webhooks": {
    "bulkJobFinished": {
      "post": {
        "operationId": "bulkJobFinishedWebhook",
        "summary": "Resultado do processamento de um lote",
        "description": "Enviado para a URL de webhook da integração (cadastrada pela equipe) quando o\nprocessamento de um lote criado pela API termina uma passagem. O evento é\n`bulk_job.<status>`; normalmente `bulk_job.completed`, `bulk_job.partial` ou\n`bulk_job.failed`.\n\nO webhook anuncia o resultado do LOTE (registros criados e créditos consumidos),\nnão a emissão do certificado: acompanhe cada registro por `/api-get-registration`.\n\nAssinatura obrigatória no cabeçalho `x-registrarobra-signature`:\n`t=<unix em segundos>,v1=<HMAC-SHA256 em hex de \"<t>.<corpo cru>\">`, com o segredo\n`ro_whsec_...` da integração. Recuse assinatura inválida ou com `t` a mais de 5\nminutos do seu relógio. Responda 2xx em até 5 segundos; redirecionamento conta como\nfalha. Até 5 tentativas (intervalos aproximados de 1 min, 5 min, 15 min e 1 h).\n",
        "parameters": [
          {
            "name": "x-registrarobra-event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "bulk_job.completed"
            }
          },
          {
            "name": "x-registrarobra-delivery",
            "in": "header",
            "required": true,
            "description": "Id desta tentativa de entrega (muda a cada tentativa).",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "x-registrarobra-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "1789999999"
            }
          },
          {
            "name": "x-registrarobra-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1789999999,v1=5d41402abc4b2a76b9719d911017c592ae2f6b1c1f6b0c1a9b2e3f4a5b6c7d8e"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookPayload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Qualquer 2xx confirma o recebimento."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "ro_live_<64 hex>",
        "description": "Token da integração, entregue uma vez pela equipe quando a integração é criada.\nTambém aceito nos cabeçalhos `x-api-key` ou `x-api-token`.\n"
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Parâmetro ausente ou malformado.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ValidationError": {
        "description": "Corpo inválido; `details.issues` lista cada problema.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Lote invalido",
              "details": {
                "issues": [
                  {
                    "field": "items[0].file_hash",
                    "message": "SHA-256 em hexadecimal minúsculo (64 caracteres)"
                  },
                  {
                    "field": "consent",
                    "message": "aceites obrigatórios ausentes: accepted_terms"
                  }
                ]
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Token ausente, inválido ou revogado.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Escopo insuficiente, acesso à API não liberado para o titular, ou titular sem permissão no workspace.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Não encontrado para esta integração.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "Corpo maior que o limite do endpoint.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Limite de requisições por minuto da integração atingido.",
        "headers": {
          "Retry-After": {
            "description": "Segundos até poder tentar de novo.",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InternalError": {
        "description": "Erro inesperado. Informe o `request_id` ao suporte.",
        "headers": {
          "x-request-id": {
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Gone": {
        "description": "Endpoint descontinuado.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string"
                },
                "deprecated": {
                  "type": "boolean",
                  "const": true
                },
                "replacement": {
                  "type": "string",
                  "example": "POST /api-submit-bulk-registration-job"
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Mensagem em português. Não dependa do texto; use o status HTTP e `details`."
          },
          "details": {
            "description": "Detalhes estruturados, quando houver.",
            "type": "object",
            "additionalProperties": true,
            "properties": {
              "issues": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "field",
                    "message"
                  ],
                  "properties": {
                    "field": {
                      "type": "string",
                      "example": "authors[0].cpf"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              },
              "file_hashes": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Sha256Hex"
                }
              },
              "required_credits": {
                "type": "integer"
              },
              "available_credits": {
                "type": "integer"
              }
            }
          },
          "request_id": {
            "type": "string",
            "format": "uuid",
            "description": "Só em erro 500."
          }
        }
      },
      "Sha256Hex": {
        "type": "string",
        "pattern": "^[a-f0-9]{64}$",
        "description": "Impressão digital SHA-256 do arquivo, em hexadecimal minúsculo (64 caracteres),\ncalculada sobre os bytes do arquivo, do lado do integrador. Maiúsculas são recusadas.\nO SHA-256 do arquivo vazio é recusado.\n",
        "example": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
      },
      "ExternalId": {
        "type": "string",
        "maxLength": 128,
        "pattern": "^[A-Za-z0-9._:-]+$"
      },
      "WorkType": {
        "type": "string",
        "description": "Categoria da obra. `marca` (Identidade visual) não substitui o registro de marca no\nINPI; `patente` (Invenção) não é patente e não substitui o depósito no INPI.\n",
        "enum": [
          "documento",
          "musica",
          "roteiro",
          "livro",
          "foto",
          "video",
          "argumento",
          "obra_artistica",
          "filme",
          "planta_arquitetura",
          "marca",
          "patente"
        ]
      },
      "Author": {
        "type": "object",
        "description": "Autoria declarada pelo titular. O RegistrarObra não verifica autoria.",
        "required": [
          "full_name",
          "cpf",
          "nationality",
          "cep",
          "state",
          "city",
          "neighborhood",
          "street",
          "number"
        ],
        "properties": {
          "full_name": {
            "type": "string",
            "maxLength": 200
          },
          "artistic_name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          },
          "cpf": {
            "type": "string",
            "description": "CPF válido (com ou sem pontuação)."
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 254,
            "description": "Obrigatório para coautores (a partir do segundo autor), que recebem convite para confirmar."
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "8 a 15 dígitos."
          },
          "nationality": {
            "type": "string",
            "maxLength": 60
          },
          "cep": {
            "type": "string",
            "description": "8 dígitos."
          },
          "state": {
            "type": "string",
            "maxLength": 60
          },
          "city": {
            "type": "string",
            "maxLength": 120
          },
          "neighborhood": {
            "type": "string",
            "maxLength": 120
          },
          "street": {
            "type": "string",
            "maxLength": 200
          },
          "number": {
            "type": "string",
            "maxLength": 20
          },
          "complement": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 120
          }
        }
      },
      "BulkItem": {
        "type": "object",
        "required": [
          "title",
          "file_name",
          "file_hash",
          "file_size"
        ],
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 160,
            "description": "Título da obra, como sai no certificado."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 600,
            "description": "Resumo próprio; sem ele, vale o do lote."
          },
          "file_name": {
            "type": "string",
            "maxLength": 255,
            "description": "Nome original do arquivo. Arquivos comprimidos (.zip, .rar, .7z, .iso...) são recusados."
          },
          "file_hash": {
            "$ref": "#/components/schemas/Sha256Hex"
          },
          "file_size": {
            "type": "integer",
            "minimum": 1,
            "maximum": 536870912000,
            "description": "Tamanho em bytes (até 500 GB)."
          },
          "file_metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Metadados técnicos opcionais do arquivo (tipo, datas). Não coloque conteúdo do arquivo aqui."
          }
        }
      },
      "BulkJobRequest": {
        "type": "object",
        "required": [
          "work_type",
          "authors",
          "items",
          "consent"
        ],
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200,
            "description": "Nome do lote."
          },
          "work_type": {
            "$ref": "#/components/schemas/WorkType"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 600,
            "description": "Resumo usado nas obras sem descrição própria."
          },
          "external_job_id": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ExternalId"
              }
            ],
            "description": "Seu identificador do pedido, para idempotência. `order_id` e `job_id` são aceitos como apelidos."
          },
          "consent": {
            "type": "object",
            "description": "Os quatro aceites do site, dados pelo titular por meio do sistema integrador. Todos\nprecisam ser `true`; ficam gravados por obra, com o id da integração.\n",
            "required": [
              "accepted_terms",
              "accepted_privacy",
              "acknowledged_original_file_responsibility",
              "acknowledged_immediate_execution_and_irreversibility"
            ],
            "properties": {
              "accepted_terms": {
                "type": "boolean",
                "const": true
              },
              "accepted_privacy": {
                "type": "boolean",
                "const": true
              },
              "acknowledged_original_file_responsibility": {
                "type": "boolean",
                "const": true
              },
              "acknowledged_immediate_execution_and_irreversibility": {
                "type": "boolean",
                "const": true
              }
            }
          },
          "authors": {
            "type": "array",
            "minItems": 1,
            "maxItems": 10,
            "items": {
              "$ref": "#/components/schemas/Author"
            }
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 25,
            "items": {
              "$ref": "#/components/schemas/BulkItem"
            }
          }
        }
      },
      "BulkJobAccepted": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "queued": {
            "type": "boolean",
            "const": true
          },
          "job_id": {
            "type": "string",
            "format": "uuid"
          },
          "external_job_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "total_items": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "const": "queued"
          }
        }
      },
      "BulkJobIdempotent": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "idempotent": {
            "type": "boolean",
            "const": true
          },
          "job_id": {
            "type": "string",
            "format": "uuid"
          },
          "external_job_id": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/JobStatus"
          },
          "total_items": {
            "type": "integer"
          },
          "completed_items": {
            "type": "integer"
          },
          "failed_items": {
            "type": "integer"
          }
        }
      },
      "JobStatus": {
        "type": "string",
        "enum": [
          "queued",
          "processing",
          "completed",
          "partial",
          "failed"
        ],
        "description": "`partial`: parte dos itens falhou. Itens `duplicate` não contam como falha."
      },
      "ItemStatus": {
        "type": "string",
        "enum": [
          "queued",
          "processing",
          "completed",
          "failed",
          "duplicate"
        ],
        "description": "`completed`: registro criado e crédito consumido. `duplicate`: o arquivo já tinha registro; nenhum crédito consumido."
      },
      "BulkJobDetails": {
        "type": "object",
        "properties": {
          "integration_id": {
            "type": "string",
            "format": "uuid"
          },
          "job": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              },
              "status": {
                "$ref": "#/components/schemas/JobStatus"
              },
              "total_items": {
                "type": "integer"
              },
              "queued_items": {
                "type": "integer"
              },
              "processing_items": {
                "type": "integer"
              },
              "completed_items": {
                "type": "integer"
              },
              "failed_items": {
                "type": "integer"
              },
              "created_registrations": {
                "type": "integer"
              },
              "error_message": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "external_job_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "updated_at": {
                "type": "string",
                "format": "date-time"
              },
              "started_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "completed_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "callback_attempts": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Tentativas de webhook deste lote."
              },
              "last_callback_sent_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "callback_last_status": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Último status HTTP recebido do seu webhook."
              },
              "callback_last_error": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "position": {
                  "type": "integer"
                },
                "title": {
                  "type": "string"
                },
                "file_name": {
                  "type": "string"
                },
                "file_hash": {
                  "$ref": "#/components/schemas/Sha256Hex"
                },
                "file_size": {
                  "type": "integer"
                },
                "status": {
                  "$ref": "#/components/schemas/ItemStatus"
                },
                "error_message": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "registration_id": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "uuid"
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "started_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "completed_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                }
              }
            }
          }
        }
      },
      "RegistrationStatus": {
        "type": "string",
        "enum": [
          "pendente",
          "processando",
          "concluido"
        ],
        "description": "`processando`: crédito consumido, emissão em andamento. `concluido`: certificado disponível."
      },
      "AnchorId": {
        "type": [
          "string",
          "null"
        ],
        "description": "Identificador interno da âncora. Trate como opaco, com uma exceção: quando começa\ncom `bitcoin-h:`, o número depois dos dois-pontos é a altura do bloco Bitcoin que\nconfirma a âncora. Qualquer outro valor significa âncora ainda não confirmada.\n",
        "example": "bitcoin-h:915000"
      },
      "Registration": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "authors": {
            "type": [
              "string",
              "null"
            ],
            "description": "Nomes da autoria declarada, em texto."
          },
          "work_type": {
            "$ref": "#/components/schemas/WorkType"
          },
          "status": {
            "$ref": "#/components/schemas/RegistrationStatus"
          },
          "payment_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Informativo. `paid` quando o crédito foi consumido."
          },
          "product_type": {
            "type": [
              "string",
              "null"
            ],
            "example": "registro"
          },
          "processing_step": {
            "type": [
              "string",
              "null"
            ],
            "description": "Etapa interna, informativa. Não dependa dos valores."
          },
          "file_hash": {
            "$ref": "#/components/schemas/Sha256Hex"
          },
          "blockchain_tx_id": {
            "$ref": "#/components/schemas/AnchorId"
          },
          "ficha_hash": {
            "type": [
              "string",
              "null"
            ],
            "description": "Impressão digital da ficha de cadastro (documento com a autoria declarada), ancorada junto."
          },
          "ficha_blockchain_tx_id": {
            "$ref": "#/components/schemas/AnchorId"
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "paid_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "bulk_job_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        }
      },
      "ArtifactLink": {
        "type": "object",
        "properties": {
          "available": {
            "type": "boolean"
          },
          "endpoint": {
            "type": "string",
            "format": "uri",
            "description": "URL absoluta de `/api-get-registration-artifact` para este documento."
          }
        }
      },
      "RegistrationResponse": {
        "type": "object",
        "properties": {
          "integration_id": {
            "type": "string",
            "format": "uuid"
          },
          "registration": {
            "$ref": "#/components/schemas/Registration"
          },
          "artifacts": {
            "type": "object",
            "description": "Documentos do registro. Outras entradas podem aparecer; ignore as que não conhecer.",
            "properties": {
              "certificate": {
                "$ref": "#/components/schemas/ArtifactLink"
              },
              "ficha": {
                "$ref": "#/components/schemas/ArtifactLink"
              }
            },
            "additionalProperties": {
              "$ref": "#/components/schemas/ArtifactLink"
            }
          }
        }
      },
      "RegistrationList": {
        "type": "object",
        "properties": {
          "integration_id": {
            "type": "string",
            "format": "uuid"
          },
          "registrations": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "title": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "work_type": {
                  "$ref": "#/components/schemas/WorkType"
                },
                "status": {
                  "$ref": "#/components/schemas/RegistrationStatus"
                },
                "payment_status": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "product_type": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "file_hash": {
                  "$ref": "#/components/schemas/Sha256Hex"
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "paid_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "completed_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "bulk_job_id": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "uuid"
                }
              }
            }
          },
          "next_before": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Cursor da próxima página; nulo quando não há mais."
          }
        }
      },
      "ArtifactResponse": {
        "type": "object",
        "properties": {
          "integration_id": {
            "type": "string",
            "format": "uuid"
          },
          "registration_id": {
            "type": "string",
            "format": "uuid"
          },
          "artifact": {
            "type": "string",
            "enum": [
              "certificate",
              "ficha"
            ]
          },
          "file_name": {
            "type": "string",
            "example": "certificado-1A2B3C4D.pdf"
          },
          "content_type": {
            "type": "string",
            "example": "application/pdf"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "URL assinada. Baixe logo; expira."
          },
          "expires_in_seconds": {
            "type": "integer",
            "example": 120
          }
        }
      },
      "VerifyResponse": {
        "type": "object",
        "required": [
          "found",
          "verification_url"
        ],
        "properties": {
          "integration_id": {
            "type": "string",
            "format": "uuid"
          },
          "found": {
            "type": "boolean"
          },
          "matched": {
            "type": "string",
            "enum": [
              "file",
              "ficha"
            ],
            "description": "`file`: é o arquivo registrado. `ficha`: é a ficha de cadastro de um registro."
          },
          "registered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "authors_declared": {
            "type": [
              "string",
              "null"
            ],
            "description": "Autoria declarada (não verificada)."
          },
          "work_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "anchor": {
            "type": "object",
            "properties": {
              "confirmed": {
                "type": "boolean"
              },
              "bitcoin_block_height": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "bitcoin_block_hash": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "bitcoin_block_time": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              }
            }
          },
          "verification_url": {
            "type": "string",
            "format": "uri",
            "description": "Página pública de verificação."
          }
        }
      },
      "HealthCheckResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "safe_test": {
            "type": "boolean",
            "const": true
          },
          "side_effects": {
            "type": "boolean",
            "const": false
          },
          "server_time": {
            "type": "string",
            "format": "date-time"
          },
          "integration": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "enum": [
                  "active"
                ]
              },
              "key_prefix": {
                "type": "string",
                "description": "Primeiros 16 caracteres do token, para identificar qual está em uso."
              },
              "scopes": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Scope"
                }
              },
              "rate_limit_per_minute": {
                "type": "integer"
              },
              "webhook_configured": {
                "type": "boolean"
              },
              "last_used_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              }
            }
          },
          "credits": {
            "type": "object",
            "properties": {
              "available": {
                "type": "integer",
                "description": "Créditos disponíveis na conta titular."
              }
            }
          },
          "checks": {
            "type": "object",
            "properties": {
              "authentication": {
                "type": "string",
                "const": "ok"
              },
              "scope": {
                "type": "string",
                "const": "health:read"
              },
              "webhook": {
                "type": "string",
                "enum": [
                  "configured",
                  "not_configured"
                ]
              }
            }
          },
          "next_recommended_calls": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Scope": {
        "type": "string",
        "enum": [
          "health:read",
          "bulk:create",
          "bulk:read",
          "registration:read",
          "artifact:read"
        ]
      },
      "WebhookPayload": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "example": "bulk_job.completed"
          },
          "integration_id": {
            "type": "string",
            "format": "uuid"
          },
          "external_job_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "delivered_at": {
            "type": "string",
            "format": "date-time"
          },
          "delivery_id": {
            "type": "string",
            "format": "uuid"
          },
          "job": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              },
              "status": {
                "$ref": "#/components/schemas/JobStatus"
              },
              "total_items": {
                "type": "integer"
              },
              "completed_items": {
                "type": "integer"
              },
              "failed_items": {
                "type": "integer"
              },
              "created_registrations": {
                "type": "integer"
              }
            }
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "position": {
                  "type": "integer"
                },
                "title": {
                  "type": "string"
                },
                "status": {
                  "$ref": "#/components/schemas/ItemStatus"
                },
                "error_message": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "registration_id": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "uuid"
                },
                "file_hash": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "completed_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                }
              }
            }
          }
        }
      }
    }
  }
}
