{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/signdocs-com-br/json-schema/signdocs-com-br-advance-session-request-schema.json",
  "title": "AdvanceSessionRequest",
  "description": "Requisição para avançar a sessão de assinatura. Os campos obrigatórios dependem da ação:\n\n| Ação | Campos obrigatórios | Descrição |\n|------|---------------------|-----------|\n| `confirm_signer` | `cpfCnpj` | Confirma a identidade declarada do signatário |\n| `accept` | — | Aceita o termo (CLICK_ACCEPT) |\n| `verify_otp` | `otpCode` | Verifica o código OTP |\n| `resend_otp` | — | Reenvia o desafio OTP |\n| `start_liveness` | — | Inicia sessão de liveness biométrico |\n| `complete_liveness` | `livenessSessionId` | Conclui liveness com resultado Rekognition |\n| `prepare_signing` | `certificateChainPems` | Prepara assinatura digital (retorna hash) |\n| `complete_signing` | `signatureRequestId`, `rawSignatureBase64` | Conclui assinatura digital |\n| `complete_document_photo` | `documentImage` | Conclui a comparação com a foto do documento (DOCUMENT_PHOTO_MATCH) |\n",
  "x-generated": "2026-10-31",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/signdocs-com-br-openapi.yaml#/components/schemas/AdvanceSessionRequest",
  "type": "object",
  "required": [
    "action"
  ],
  "properties": {
    "action": {
      "type": "string",
      "description": "Ação a executar na sessão.",
      "enum": [
        "confirm_signer",
        "accept",
        "verify_otp",
        "resend_otp",
        "start_liveness",
        "complete_liveness",
        "prepare_signing",
        "complete_signing",
        "complete_document_photo"
      ]
    },
    "cpfCnpj": {
      "type": "string",
      "description": "CPF ou CNPJ que o signatário digita para confirmar a própria\nidentidade (obrigatório em `confirm_signer`). Comparado com o valor\ninformado na criação da sessão; divergência reprova a etapa.\n"
    },
    "documentImage": {
      "type": "string",
      "format": "byte",
      "description": "Foto do documento de identidade em base64 (obrigatório em\n`complete_document_photo`). Máximo 5 MB. O rosto é extraído da\nimagem e comparado com a referência do cadastro.\n"
    },
    "documentType": {
      "type": "string",
      "description": "Tipo do documento enviado em `documentImage`."
    },
    "deviceInfo": {
      "type": "object",
      "description": "Características do dispositivo do signatário, gravadas na evidência\njunto da geolocalização. Todos os campos são opcionais.\n",
      "properties": {
        "screenWidth": {
          "type": "integer"
        },
        "screenHeight": {
          "type": "integer"
        },
        "language": {
          "type": "string"
        },
        "platform": {
          "type": "string"
        },
        "touchPoints": {
          "type": "integer"
        }
      }
    },
    "otpCode": {
      "type": "string",
      "description": "Código OTP de 6 dígitos (obrigatório para `verify_otp`)."
    },
    "otpChannel": {
      "allOf": [
        {
          "$ref": "#/$defs/OtpChannel"
        }
      ],
      "description": "Canal escolhido pelo signatário para receber o OTP — na ação `accept` (escolha inicial) ou `resend_otp` (troca de canal). Aceito apenas quando a sessão foi criada com `signer.otpChannelSelectable`; caso contrário a requisição é rejeitada com 400."
    },
    "livenessSessionId": {
      "type": "string",
      "description": "ID da sessão Rekognition (obrigatório para `complete_liveness`)."
    },
    "certificateChainPems": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Cadeia de certificados PEM (obrigatório para `prepare_signing`)."
    },
    "signatureRequestId": {
      "type": "string",
      "description": "ID da requisição de assinatura (obrigatório para `complete_signing`)."
    },
    "rawSignatureBase64": {
      "type": "string",
      "format": "byte",
      "description": "Assinatura bruta em base64 (obrigatório para `complete_signing`)."
    },
    "sandboxBrightness": {
      "type": "number",
      "format": "double",
      "minimum": 0,
      "maximum": 100,
      "description": "Brilho simulado da captura, para ensaiar a reprovação por qualidade\n(`DOCUMENT_QUALITY_LOW`). Mesmo enquadramento do `sandboxSimilarity`:\nsó vale em sandbox.\n\nAntes destes campos as ramificações de sandbox fixavam 80/80 e nunca\navaliavam o piso de qualidade — ou seja, o erro que mais aparece em\ncampo (luz ruim, reflexo no visor, sujeito em movimento) era o único\nque o integrador não conseguia reproduzir sob demanda.\n"
    },
    "sandboxSharpness": {
      "type": "number",
      "format": "double",
      "minimum": 0,
      "maximum": 100,
      "description": "Nitidez simulada da captura. Ver `sandboxBrightness`.\n"
    },
    "sandboxLivenessConfidence": {
      "type": "number",
      "format": "double",
      "minimum": 0,
      "maximum": 100,
      "description": "Confiança de liveness simulada, para ensaiar a reprovação. Mesmo\nenquadramento do `sandboxSimilarity`: só vale em sandbox, e nunca\naprova uma verificação real.\n\nSubstitui o truque antigo de escrever `fail` no `livenessSessionId`,\nque só produzia 99.5 ou 30.0 — nenhum dos dois perto do limiar, que é\njustamente onde as capturas reais caem. A convenção antiga continua\nvalendo quando nada é informado.\n"
    },
    "sandboxSimilarity": {
      "type": "number",
      "format": "double",
      "minimum": 0,
      "maximum": 100,
      "description": "Similaridade simulada para as etapas biométricas em sandbox, usada\npara exercitar o caminho de reprovação. Padrão 99.5, que aprova;\nabaixo do limiar do tenant, a etapa reprova como reprovaria em\nprodução.\n\nSó é lida quando a etapa já resolveu para sandbox — em HML, ou em\ntenants com `mode: SANDBOX` — e quando não existe frame real de\nliveness. Uma captura real sempre vence: a comparação real acontece\ne este campo é ignorado. Ou seja, ele nunca faz uma comparação real\npassar.\n\nA página hospedada é quem chama o advance, então o caminho usual\nnão é este campo, e sim `metadata.sandboxSimilarity` na criação da\nsessão. Envie aqui apenas em integrações headless, que chamam o\nadvance diretamente; o corpo tem precedência sobre a metadata. Um\nvalor fora de 0-100 ou não numérico retorna 400.\n"
    },
    "geolocation": {
      "type": "object",
      "description": "Geolocalização do dispositivo. Aceito nas ações `accept`, `verify_otp`,\n`complete_liveness` e `complete_signing`. Obrigatório em `complete_liveness`\nquando o tenant possui `biometricRequired` habilitado.\n",
      "properties": {
        "latitude": {
          "type": "number",
          "format": "double",
          "description": "Latitude em graus decimais (-90 a 90)."
        },
        "longitude": {
          "type": "number",
          "format": "double",
          "description": "Longitude em graus decimais (-180 a 180)."
        },
        "accuracy": {
          "type": "number",
          "format": "double",
          "description": "Precisão em metros (>= 0)."
        },
        "source": {
          "type": "string",
          "description": "Origem da geolocalização.",
          "enum": [
            "GPS",
            "IP",
            "WIFI",
            "CELL"
          ]
        }
      }
    }
  },
  "$defs": {
    "OtpChannel": {
      "type": "string",
      "description": "Canal de entrega do OTP, escolhido por signatário. Independente do canal\nusado para entregar o link de assinatura: um convite por WhatsApp pode ter\nOTP por e-mail, e um convite por e-mail pode ter OTP por WhatsApp.\n\n`sms` e `whatsapp` exigem `signer.phone` e o canal habilitado para o tenant\n(`sms.enabled` / `whatsapp.enabled`); caso contrário o passo de OTP falha com\n**422**. No WhatsApp, um número que pediu para não receber mensagens também\nfalha com 422, com uma mensagem que orienta a escolher outro canal.\n\n`telegram` exige `telegram.enabled` no tenant. O código chega pelo bot da\nSignDocs quando o signatário já registrou o próprio CPF nele; sem registro, é\nenviado por telefone (Telegram Gateway) se o tenant tiver essa opção\nhabilitada, e caso contrário o passo falha com **422** de imediato. Ao criar a\nsessão, `otpChannel: telegram` ainda exige `signer.email` quando a política\ntem OTP.\n\nUm OTP por WhatsApp ou pelo Telegram Gateway consome a cota de mensagens do\ntenant; esgotada, o envio é recusado com **429**.\n",
      "enum": [
        "email",
        "sms",
        "whatsapp",
        "telegram"
      ],
      "default": "email"
    }
  }
}