Guias de Desenvolvimento

Signing Sessions

Guias dos SDKs

Tratamento de Erros

A API retorna erros no formato RFC 7807 Problem Details. Todos os SDKs mapeiam os erros para exceções tipadas, facilitando o tratamento específico por tipo de erro.


ProblemDetail (RFC 7807)

Toda resposta de erro da API segue este formato:

{
  "type": "https://api.signdocs.com.br/errors/not-found",
  "title": "Not Found",
  "status": 404,
  "detail": "Transaction tx-abc-123 not found",
  "instance": "/v1/transactions/tx-abc-123"
}
Campo Descrição
type URI que identifica o tipo de erro
title Resumo legível do erro
status Código HTTP
detail Explicação específica desta ocorrência
instance URI da requisição que causou o erro

Exceções por status HTTP

Status Exceção Descrição
400 BadRequestError Requisição malformada ou parâmetros inválidos
401 UnauthorizedError Token ausente, expirado ou inválido
403 ForbiddenError Sem permissão para o recurso
404 NotFoundError Recurso não encontrado
409 ConflictError Conflito de estado (ex: transação já finalizada)
422 UnprocessableEntityError Dados válidos sintaticamente mas semanticamente incorretos
429 RateLimitError Limite de requisições excedido
500 InternalServerError Erro interno do servidor
503 ServiceUnavailableError Serviço temporariamente indisponível

Erros de infraestrutura (não-HTTP):

Exceção Descrição
AuthenticationError Falha na obtenção do token OAuth2
ConnectionError Falha de conexão de rede
TimeoutError Timeout da requisição ou duração máxima de retry excedida

Tratamento por tipo de erro

import {
  NotFoundError,
  RateLimitError,
  ConflictError,
  SignDocsBrasilApiError,
  SignDocsBrasilError,
} from '@signdocs-brasil/api';

try {
  const tx = await client.transactions.get('tx-inexistente');
} catch (err) {
  if (err instanceof NotFoundError) {
    console.log('Não encontrado:', err.detail);
  } else if (err instanceof RateLimitError) {
    console.log('Rate limit, retry em:', err.retryAfterSeconds, 'segundos');
  } else if (err instanceof ConflictError) {
    console.log('Conflito:', err.detail);
  } else if (err instanceof SignDocsBrasilApiError) {
    console.log('Erro de API:', err.status, err.title, err.detail);
    console.log('ProblemDetail:', err.problemDetail);
  } else if (err instanceof SignDocsBrasilError) {
    console.log('Erro de infraestrutura:', err.message);
  }
}
from signdocs_brasil.errors import (
    NotFoundError,
    RateLimitError,
    ConflictError,
    SignDocsBrasilApiError,
    SignDocsBrasilError,
)

try:
    tx = client.transactions.get('tx-inexistente')
except NotFoundError as e:
    print(f'Não encontrado: {e.problem_detail.detail}')
except RateLimitError as e:
    print(f'Rate limit, retry em: {e.retry_after_seconds}s')
except ConflictError as e:
    print(f'Conflito: {e.problem_detail.detail}')
except SignDocsBrasilApiError as e:
    print(f'Erro de API: {e.problem_detail.status} {e.problem_detail.title}')
except SignDocsBrasilError as e:
    print(f'Erro de infraestrutura: {e}')
import (
    "errors"
    signdocs "github.com/signdocsbrasil/signdocsbrasil-go"
)

tx, err := client.Transactions.Get(ctx, "tx-inexistente")
if err != nil {
    var notFound *signdocs.NotFoundError
    var rateLimit *signdocs.RateLimitError
    var conflict *signdocs.ConflictError
    var apiErr *signdocs.ApiError

    if errors.As(err, &notFound) {
        fmt.Println("Não encontrado:", notFound.ProblemDetail.Detail)
    } else if errors.As(err, &rateLimit) {
        fmt.Println("Rate limit, retry em:", rateLimit.RetryAfterSeconds, "segundos")
    } else if errors.As(err, &conflict) {
        fmt.Println("Conflito:", conflict.ProblemDetail.Detail)
    } else if errors.As(err, &apiErr) {
        fmt.Println("Erro de API:", apiErr.StatusCode, apiErr.ProblemDetail.Title)
    } else {
        fmt.Println("Erro:", err)
    }
}

// Funções auxiliares
if signdocs.IsNotFound(err) { /* ... */ }
if signdocs.IsRateLimit(err) { /* ... */ }
if signdocs.IsConflict(err) { /* ... */ }
import com.signdocsbrasil.api.exceptions.*;

try {
    Transaction tx = client.transactions().get("tx-inexistente");
} catch (NotFoundException e) {
    System.out.println("Não encontrado: " + e.getProblemDetail().getDetail());
} catch (RateLimitException e) {
    System.out.println("Rate limit, retry em: " + e.getRetryAfterSeconds() + "s");
} catch (ConflictException e) {
    System.out.println("Conflito: " + e.getProblemDetail().getDetail());
} catch (ApiException e) {
    System.out.println("Erro de API: " + e.getStatus() + " " + e.getTitle());
} catch (SignDocsBrasilException e) {
    System.out.println("Erro de infraestrutura: " + e.getMessage());
}
use SignDocsBrasil\Api\Exceptions\NotFoundException;
use SignDocsBrasil\Api\Exceptions\RateLimitException;
use SignDocsBrasil\Api\Exceptions\ConflictException;
use SignDocsBrasil\Api\Exceptions\ApiException;
use SignDocsBrasil\Api\Exceptions\SignDocsBrasilException;

try {
    $tx = $client->transactions->get('tx-inexistente');
} catch (NotFoundException $e) {
    echo 'Não encontrado: ' . $e->getProblemDetail()->detail;
} catch (RateLimitException $e) {
    echo 'Rate limit, retry em: ' . $e->retryAfterSeconds . 's';
} catch (ConflictException $e) {
    echo 'Conflito: ' . $e->getProblemDetail()->detail;
} catch (ApiException $e) {
    echo 'Erro de API: ' . $e->getStatus() . ' ' . $e->getTitle();
} catch (SignDocsBrasilException $e) {
    echo 'Erro de infraestrutura: ' . $e->getMessage();
}

Comportamento de Retry

Os SDKs fazem retry automaticamente em erros transientes:

Parâmetro Valor padrão
Status codes com retry 429, 500, 503
Máximo de tentativas 5
Backoff Exponencial: 2^tentativa + jitter aleatório (0-1s)
Delay máximo por tentativa 30 segundos
Duração total máxima 60 segundos
Header Retry-After Respeitado quando presente
Idempotência X-Idempotency-Key gerado automaticamente em criação de transações

O retry é transparente — se todas as tentativas falharem, a exceção correspondente ao último status é lançada.


Boas práticas

  1. Sempre trate NotFoundError em operações de leitura
  2. Trate ConflictError ao finalizar transações (pode já estar finalizada)
  3. Não implemente retry manual para 429/500/503 — o SDK já faz automaticamente
  4. Use idempotency keys em operações de criação para evitar duplicatas em caso de retry
  5. Monitore RateLimitError em produção para ajustar volume de requisições