Skip to content

Contract API - Especificação de Endpoints

Visão Geral

API RESTful para persistência do contrato durante o fluxo de criação. O contrato é salvo como snapshot completo (todos os dados dos steps) através de um botão manual na toolbar.


Endpoints

1. Criar Contrato (Draft)

POST /contracts

Cria um novo contrato em status draft com todos os dados atuais dos steps.

Request Body:

typescript
{
  name: string                          // Nome do contrato (obrigatório)
  workflowId: string                    // ID do workflow selecionado
  currentStep: {
    key: StepKey                        // Key do step atual
    index: number                       // Índice do step atual
  }
  steps: ContractStep[]                 // Snapshot dos steps com status/progress
  parts: ContractPart[]                 // Partes adicionadas (sem id no POST)
  selectedTemplate?: Template                 // Template selecionado (objeto completo)
  modelConfig?: {
    templateId: string
    partGroups: PartGroup[]
    variableAssignments: VariableAssignment[]
  }
  variableValues?: VariableValue[]
  partGroups?: PartGroup[]                    // Grupos de partes configurados
  documents: DocumentRequirement[]      // Documentos configurados
  revisionConfig?: {
    reviewers: ReviewerMember[]
  }
  signatureConfig?: {
    groups: SignatureGroup[]
  }
}

Response: 201 Created

typescript
{
  id: string // UUID gerado pelo backend
  name: string
  status: 'draft'
  progress: number
  createdAt: Date
  updatedAt: Date
}

2. Atualizar Contrato

PUT /contracts/:id

Atualiza um contrato existente com o snapshot completo dos dados atuais.

Request Body: Mesmo schema do POST. O id não é enviado no body, vai na URL.

Response: 200 OK

typescript
{
  id: string
  name: string
  status: ContractStatus
  progress: number
  updatedAt: Date
}

3. Carregar Contrato Completo (para edição)

GET /contracts/:id/detail

Retorna o contrato com todos os dados necessários para restaurar o estado no frontend.

Response: 200 OK

typescript
{
  id: string
  name: string
  status: ContractStatus
  progress: number
  workflowId: string
  currentStep: {
    key: StepKey
    index: number
  }
  steps: ContractStep[]
  parts: ContractPart[]
  selectedTemplate?: Template
  modelConfig?: ModelStepConfig
  variableValues?: VariableValue[]
  partGroups?: PartGroup[]
  documents: DocumentRequirement[]
  revisionConfig?: RevisionStepConfig
  signatureConfig?: SignatureStepConfig
  uploadedFiles: UploadedFile[]         // Arquivos já enviados no step Upload
  createdAt: Date
  updatedAt: Date
}

4. Upload de Arquivo (Step Upload)

POST /contracts/:id/documents/:docId/files

Upload de arquivo para um documento específico. Usa multipart/form-data.

Request:

Content-Type: multipart/form-data

file: File                              // Arquivo binário
partId: string                          // ID da parte que está enviando

Response: 201 Created

typescript
{
  id: string // UUID do arquivo
  documentId: string
  partId: string
  fileName: string
  fileUrl: string
  fileSize: number
  uploadedAt: Date
}

5. Remover Arquivo Enviado

DELETE /contracts/:id/documents/:docId/files/:fileId

Response: 204 No Content


6. Completar Step (já existe)

PATCH /contracts/:id/steps/:stepKey/complete

Marca um step como completo e avança o fluxo.

Response: 200 OK

typescript
{
  success: true
  stepKey: string
}

7. Transição de Status

PATCH /contracts/:id/status

Endpoint dedicado para transição de status do contrato. Recebe apenas o novo status.

Request Body:

typescript
{
  status: string // Valores aceitos (uppercase): DRAFT, IN_REVIEW, PUBLISHED, ARCHIVED, DONE
}

Response: 200 OK

typescript
{
  id: string
  status: string
}

Importante: O frontend armazena status em lowercase (draft, in_review, published, etc.). O service converte para uppercase antes de enviar para a API. O backend deve aceitar os seguintes valores:

  • DRAFT: rascunho
  • IN_REVIEW: em revisão (enviado para revisores)
  • PUBLISHED: publicado (enviado para partes, upload ou assinatura)
  • ARCHIVED: arquivado
  • DONE: finalizado

Transição: draftin_review

O backend deve:

  1. Validar que o contrato está em status draft
  2. Alterar o status para in_review
  3. Disparar e-mail para todos os revisores configurados em revisionConfig.reviewers com:
    • Link do contrato para revisão: {APP_URL}/contracts/{id}/review
    • Nome do contrato
    • Nome de quem enviou
    • Prazo (se configurado no workflow)
  4. Registrar entrada no histórico

Erros específicos:

  • 422: Nenhum revisor configurado em revisionConfig

Transição: draft ou in_reviewpublished

O backend deve:

  1. Validar que o contrato está em status draft ou in_review
  2. Alterar o status para published
  3. Identificar o próximo step pendente e executar a rotina:

Se o step atual for upload:

  • Disparar e-mail/notificação para todas as partes (parts) do contrato com:
    • Link da página de preenchimento: {APP_URL}/contracts/{id}/fill
    • Nome do contrato
    • Lista de documentos pendentes de upload
    • Prazo (se configurado no workflow)

Se o step atual for signature:

  • Disparar e-mail/notificação para todas as partes (parts) do contrato com:
    • Link da página de assinatura: {APP_URL}/contracts/{id}/sign
    • Nome do contrato
    • Instruções de assinatura
    • Prazo (se configurado no workflow)
  1. Registrar entrada no histórico

Erros específicos:

  • 422: Dados obrigatórios do step atual não preenchidos

Resumo: Transições de Status

Status anteriorStatus enviadoRotina do backendNotificação
draftin_reviewValidar revisores, avançar fluxoE-mail para revisores com link de revisão
draft / in_reviewpublishedValidar dados, avançar fluxoE-mail para partes com link de upload ou assinatura

Tipos de Referência

SaveContractPayload

typescript
type SaveContractPayload = {
  name: string
  workflowId: string
  status?: ContractStatus
  currentStep: { key: string; index: number }
  steps: ContractStep[]
  parts: Omit<ContractPart, 'id'>[]
  selectedTemplate?: Template
  modelConfig?: ModelStepConfig
  variableValues?: VariableValue[]
  partGroups?: PartGroup[]
  documents: DocumentRequirement[]
  revisionConfig?: RevisionStepConfig
  signatureConfig?: SignatureStepConfig
}

ContractDetail

typescript
type ContractDetail = {
  id: string
  name: string
  status: ContractStatus
  progress: number
  workflowId: string
  currentStep: { key: string; index: number }
  steps: ContractStep[]
  parts: ContractPart[]
  selectedTemplate?: Template
  modelConfig?: ModelStepConfig
  variableValues?: VariableValue[]
  partGroups?: PartGroup[]
  documents: DocumentRequirement[]
  revisionConfig?: RevisionStepConfig
  signatureConfig?: SignatureStepConfig
  uploadedFiles: UploadedFile[]
  createdAt: Date
  updatedAt: Date
}

VariableValue

typescript
type VariableValue = {
  id: string
  name: string
  type: string
  source: string
  required: boolean
  value: string
  additional?: string
  partId?: string
  groupId?: string
}

UploadedFile

typescript
type UploadedFile = {
  id: string
  documentId: string
  partId: string
  fileName: string
  fileUrl: string
  fileSize: number
  uploadedAt: Date
}

Regras de Negócio

  1. O contrato é criado em status draft e só muda de status via ações explícitas
  2. O progress é calculado no frontend (média dos steps) e enviado ao backend
  3. O step de Upload é auto-gerenciado: aparece quando há documentos com isDownload: false
  4. Upload de arquivos é feito via endpoint separado (multipart), não faz parte do save geral
  5. Todos os IDs devem ser UUIDs no formato xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
  6. O backend deve aceitar saves parciais, nem todos os campos precisam estar preenchidos
  7. O frontend faz mapeamento dos steps da API (label/completed) para o formato interno (name/status/progress/isCurrent/isEnabled)
  8. O frontend faz mapeamento das parts da API (flat com type/name/email) para o formato interno (person: Person + role)

8. Overview de Uploads por Prestador

GET /contracts/:id/uploads

Retorna overview de uploads agrupado por prestador (provider). Usado na tela de validação de documentos enviados.

Response: 200 OK

typescript
{
  providers: Array<{
    total: number
    approved: number
    rejected: number
    provider: TeamMember // Objeto do membro da equipe no padrão da aplicação
    documents: Array<{
      id: string
      documentId: string
      name: string
      fileName: string
      fileUrl: string
      fileSize: number
      status: 'pending' | 'approved' | 'rejected'
      rejectionReason?: string
      uploadedAt: Date
    }>
  }>
  allApproved: boolean
}

Regras:

  • Apenas prestadores (providers) são incluídos, não o borrower
  • allApproved é true quando todos os documentos de todos os providers estão com status approved
  • total = quantidade de documentos com isDownload: false no contrato

Erros:

  • 404: contrato não encontrado

9. Revisar Documento Enviado

PATCH /contracts/:id/documents/:docId/review

Aprova ou rejeita um documento enviado por um prestador.

Request Body:

typescript
{
  status: 'approved' | 'rejected'
  reason?: string                       // Obrigatório quando status = 'rejected'
}

Response: 200 OK, retorna o documento atualizado:

typescript
{
  id: string
  documentId: string
  name: string
  fileName: string
  fileUrl: string
  fileSize: number
  status: 'approved' | 'rejected'
  rejectionReason?: string
  uploadedAt: Date
}

Regras:

  • Quando status = 'rejected', o campo reason é obrigatório
  • Ao rejeitar, o backend deve notificar o prestador para reenviar o documento
  • Ao aprovar, registrar entrada no histórico

Erros:

  • 422: reason obrigatório quando status = 'rejected'
  • 404: documento não encontrado

10. Overview de Revisão

GET /contracts/:id/review

Retorna overview completo da revisão com contratos gerados (HTML com variáveis substituídas por parte/grupo) e status dos revisores.

Response: 200 OK

typescript
{
  contracts: Array<{
    id: string // UUID do item de revisão
    groupId: string // ID do grupo/parte
    groupName: string // Nome do grupo
    partName: string // Nome da parte
    htmlContent: string // HTML do contrato com variáveis substituídas
    thumbnailUrl?: string // Thumbnail (opcional)
    status: 'pending' | 'approved' | 'rejected' | 'changes_requested'
    commentsCount: number
    approvals: Array<{
      reviewerId: string
      status: 'pending' | 'approved' | 'rejected'
      comment?: string
      reviewedAt?: Date
    }>
    totalRequired: number // Total de revisores obrigatórios
    approvedCount: number // Quantos obrigatórios já aprovaram
  }>
  reviewers: Array<{
    id: string
    userId: string
    name: string
    email: string
    avatar?: string
    isRequired: boolean
    order: number
    totalContracts: number // Total de contratos para revisar
    reviewedCount: number // Quantos já revisou
    approvedCount: number
    rejectedCount: number
  }>
  allApproved: boolean // true quando todos os revisores obrigatórios aprovaram todos os contratos
}

Regras:

  • contracts são gerados a partir dos partGroups do contrato, o htmlContent é o template HTML com variáveis substituídas pelos valores atribuídos na etapa de modelo
  • allApproved é true quando todos os revisores com isRequired: true aprovaram todos os contratos
  • totalRequired conta apenas revisores obrigatórios
  • status do contrato é calculado: approved quando todos obrigatórios aprovaram, rejected se algum rejeitou, changes_requested se há rejeição com pendência, pending caso contrário
  • Deve haver pelo menos 1 revisor, não é permitido remover o último

Erros:

  • 404: contrato não encontrado

11. Adicionar Comentário a um Contrato em Revisão

O reviewer seleciona um trecho de texto no HTML do contrato e adiciona um comentário vinculado a esse trecho. Apenas reviewers do contrato podem comentar.

POST /contracts/:id/review/:contractItemId/comment

Request Body:

typescript
{
  content: string                           // Texto do comentário
  selectedText?: string                     // Trecho de texto selecionado no contrato
  startOffset?: number                      // Offset inicial da seleção (opcional)
  endOffset?: number                        // Offset final da seleção (opcional)
  pageNumber?: number                       // Número da página (opcional, legado)
}

Response: 201 Created

typescript
{
  id: string
  reviewerId: string
  reviewerName: string
  reviewerEmail: string
  reviewerAvatar?: string
  content: string
  selectedText?: string
  startOffset?: number
  endOffset?: number
  pageNumber?: number
  createdAt: Date
  updatedAt?: Date
}

11.1. Editar Comentário

Apenas o autor do comentário pode editá-lo.

PATCH /contracts/:id/review/:contractItemId/comments/:commentId

Request Body:

typescript
{
  content: string // Novo texto do comentário
}

Response: 200 OK, retorna ReviewComment atualizado


12. Listar Comentários de um Contrato em Revisão

GET /contracts/:id/review/:contractItemId/comments

Response: 200 OK

typescript
Array<{
  id: string
  reviewerId: string
  reviewerName: string
  reviewerEmail: string
  reviewerAvatar?: string
  content: string
  selectedText?: string
  startOffset?: number
  endOffset?: number
  pageNumber?: number
  createdAt: Date
  updatedAt?: Date
}>

13. Aprovar Contrato em Revisão

PATCH /contracts/:id/review/:contractItemId/approve

Request Body:

typescript
{
  comment?: string                          // Comentário opcional ao aprovar
}

Response: 200 OK

typescript
{
  success: true
  status: 'approved'
}

Regras:

  • Registra a aprovação do revisor atual para o contrato específico
  • Recalcula allApproved global
  • Registra entrada no histórico

14. Rejeitar Contrato em Revisão

PATCH /contracts/:id/review/:contractItemId/reject

Request Body:

typescript
{
  reason: string // Motivo da rejeição (obrigatório)
}

Response: 200 OK

typescript
{
  success: true
  status: 'rejected'
}

Regras:

  • reason é obrigatório
  • Notifica os responsáveis sobre a rejeição

Erros:

  • 422: reason obrigatório

15. Adicionar Revisor

POST /contracts/:id/review/reviewers

Request Body:

typescript
{
  userId: string
  name: string
  email: string
  isRequired: boolean
}

Response: 201 Created, retorna ReviewerSummary


16. Remover Revisor

DELETE /contracts/:id/review/reviewers/:reviewerId

Response: 204 No Content

Regras:

  • Não é permitido remover o último revisor, deve haver pelo menos 1

Erros:

  • 422: não é possível remover o último revisor

17. Enviar Lembrete de Atenção a Revisor

POST /contracts/:id/review/reviewers/:reviewerId/reminder

Envia notificação (email) ao revisor lembrando da revisão pendente.

Response: 200 OK

typescript
{
  success: true
}

Signature API

Endpoints para gerenciar assinaturas do contrato. Os grupos de assinatura são derivados do signatureConfig do workflow atrelado ao contrato.


18. Overview de Assinaturas

GET /contracts/:id/signatures

Retorna os grupos de assinatura configurados e o status atual de cada signatário.

Response: 200 OK

typescript
{
  groups: Array<{
    id: string
    name: string
    order: number
    isPartGroup: boolean
    members: Array<{
      id: string
      userId?: string
      name: string
      email: string
      avatar?: string
      order: number
      status: 'pending' | 'signed' | 'rejected'
      signedAt?: Date
      rejectionReason?: string
    }>
  }>
  summary: {
    totalSigners: number
    signedCount: number
    pendingCount: number
    rejectedCount: number
    allSigned: boolean
  }
}

Regras:

  • Os grupos são inicializados a partir do signatureConfig do workflow
  • O grupo com isPartGroup: true é populado automaticamente com as partes do contrato (role = 'provider')
  • A ordem de assinatura é definida pelo campo order de cada grupo
  • allSigned é true quando todos os signatários assinaram

Erros:

  • 404: contrato não encontrado

19. Atualizar Configuração de Assinaturas

PUT /contracts/:id/signatures

Atualiza os grupos de assinatura. Usado para adicionar/remover signatários internos antes de iniciar o fluxo.

Request Body:

typescript
{
  groups: Array<{
    id: string
    name: string
    order: number
    isPartGroup: boolean
    members: Array<{
      id: string
      userId?: string
      name: string
      email: string
      order: number
    }>
  }>
}

Response: 200 OK, retorna ContractSignaturesResponse

Regras:

  • Grupos com isPartGroup: true não podem ser removidos
  • A ordem dos grupos determina a sequência de assinatura (campo order)
  • Membros só podem ser adicionados/removidos em grupos com isPartGroup: false

20. Iniciar Fluxo de Assinaturas

POST /contracts/:id/signatures/start

Inicia o fluxo de assinaturas notificando os primeiros signatários conforme a ordem configurada.

Response: 200 OK

typescript
{
  message: string
  notifiedSigners: Array<{
    id: string
    name: string
    email: string
  }>
}

Regras:

  • Dispara notificação (email) para os signatários do grupo com menor order
  • Após todos do grupo assinarem, notifica o próximo grupo na ordem
  • Registra entrada no histórico

Erros:

  • 422: nenhum signatário configurado

21. Assinar Contrato

POST /contracts/:id/signatures/:signerId/sign

Registra a assinatura de um signatário.

Request Body:

typescript
{
  signatureData?: string      // Base64 da imagem da assinatura (se manual)
  signatureType?: 'electronic' | 'digital'
  ipAddress?: string          // IP do signatário (capturado pelo frontend)
  userAgent?: string          // User agent do navegador
}

Response: 200 OK

typescript
{
  id: string
  status: 'signed'
  signedAt: string // ISO 8601
  nextSigners: Array<{
    // Próximos signatários notificados
    id: string
    name: string
    email: string
  }>
}

Regras:

  • Valida que é a vez do signatário assinar (conforme ordem)
  • Registra timestamp, IP e user agent para auditoria
  • Se todos do grupo atual assinaram, notifica o próximo grupo
  • Se todos assinaram, atualiza status do contrato para completed
  • Registra entrada no histórico

Erros:

  • 422: não é a vez deste signatário
  • 404: signatário não encontrado

22. Rejeitar Assinatura

POST /contracts/:id/signatures/:signerId/reject

Registra a recusa de um signatário em assinar o contrato.

Request Body:

typescript
{
  reason: string // Motivo da rejeição (obrigatório)
}

Response: 200 OK

typescript
{
  id: string
  status: 'rejected'
  rejectionReason: string
  rejectedAt: string // ISO 8601
}

Regras:

  • reason é obrigatório
  • Notifica os administradores sobre a rejeição
  • O fluxo de assinaturas é pausado até resolução
  • Registra entrada no histórico

Erros:

  • 422: reason obrigatório
  • 404: signatário não encontrado

23. Enviar Lembrete ao Signatário

POST /contracts/:id/signatures/:signerId/reminder

Envia notificação (email) ao signatário lembrando da assinatura pendente.

Response: 200 OK

typescript
{
  message: string
  sentTo: string // Email do destinatário
}

Regras:

  • Só pode enviar lembrete para signatários com status pending
  • Limite de 1 lembrete por hora por signatário (opcional, configurável)

Tipos de Referência (Signature)

ContractSignaturesResponse

typescript
type ContractSignaturesResponse = {
  groups: ContractSignatureGroup[]
  summary: ContractSignaturesSummary
}

ContractSignatureGroup

typescript
type ContractSignatureGroup = {
  id: string
  name: string
  order: number
  isPartGroup: boolean
  members: ContractSignerMember[]
}

ContractSignerMember

typescript
type ContractSignerMember = {
  id: string
  userId?: string
  name: string
  email: string
  avatar?: string
  order: number
  status: 'pending' | 'signed' | 'rejected'
  signedAt?: Date
  rejectionReason?: string
}

ContractSignaturesSummary

typescript
type ContractSignaturesSummary = {
  totalSigners: number
  signedCount: number
  pendingCount: number
  rejectedCount: number
  allSigned: boolean
}

UpdateSignaturesPayload

typescript
type UpdateSignaturesPayload = {
  groups: Array<{
    id: string
    name: string
    order: number
    isPartGroup: boolean
    members: Array<{
      id: string
      userId?: string
      name: string
      email: string
      order: number
    }>
  }>
}