{
  "openapi": "3.1.0",
  "info": {
    "title": "Sua Carta API",
    "version": "1.0.0",
    "summary": "API para disparo e acompanhamento de cartas manuscritas.",
    "description": "API REST para criar cartas individuais ou em lote, consultar status operacionais e receber eventos por webhook.",
    "contact": {
      "name": "Suporte de integração",
      "email": "contato@suacarta.com.br",
      "url": "https://suacarta.com.br/integracoes/api"
    }
  },
  "servers": [
    {
      "url": "https://api.suacarta.com.br/v1",
      "description": "Produção"
    }
  ],
  "tags": [
    {
      "name": "Cards",
      "description": "Criação e consulta de cartas individuais."
    },
    {
      "name": "Batches",
      "description": "Envio em lote para campanhas e rotinas recorrentes."
    },
    {
      "name": "Catalog",
      "description": "Templates e estilos disponíveis para a conta."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API Key",
        "description": "Envie sua API key no header Authorization: Bearer <token>."
      }
    },
    "parameters": {
      "CardId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Identificador único da carta.",
        "schema": {
          "type": "string",
          "example": "card_82x9a1"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Chave estável para garantir que retries do mesmo evento de negócio não criem cartas duplicadas.",
        "schema": {
          "type": "string",
          "example": "welcome-user_92811"
        }
      }
    },
    "headers": {
      "XRateLimitLimit": {
        "description": "Limite de requisições liberado para a janela atual.",
        "schema": {
          "type": "integer",
          "example": 60
        }
      },
      "XRateLimitRemaining": {
        "description": "Saldo de requisições restante na janela atual.",
        "schema": {
          "type": "integer",
          "example": 42
        }
      },
      "XRateLimitReset": {
        "description": "Unix timestamp indicando quando a janela atual será renovada.",
        "schema": {
          "type": "integer",
          "example": 1713385200
        }
      },
      "XSuaCartaSignature": {
        "description": "Assinatura HMAC SHA-256 do payload bruto do webhook.",
        "schema": {
          "type": "string",
          "example": "58bff9878c5c0fd53f3c5a4d74f56fcf012d55a7830fbd6d1a07b42f66e7cf44"
        }
      },
      "XSuaCartaTimestamp": {
        "description": "Unix timestamp usado na composição da assinatura do webhook.",
        "schema": {
          "type": "integer",
          "example": 1713381600
        }
      },
      "XSuaCartaEvent": {
        "description": "Nome do evento entregue via webhook.",
        "schema": {
          "type": "string",
          "example": "card.shipped"
        }
      }
    },
    "schemas": {
      "Address": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "address_line1",
          "city",
          "state",
          "zip"
        ],
        "properties": {
          "name": {
            "type": "string",
            "example": "Maria Silva"
          },
          "address_line1": {
            "type": "string",
            "example": "Rua das Flores, 123"
          },
          "address_line2": {
            "type": [
              "string",
              "null"
            ],
            "example": "Apto 91"
          },
          "city": {
            "type": "string",
            "example": "São Paulo"
          },
          "state": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "example": "SP"
          },
          "zip": {
            "type": "string",
            "example": "01234-567"
          },
          "country": {
            "type": "string",
            "default": "BR",
            "example": "BR"
          }
        }
      },
      "CardStatus": {
        "type": "string",
        "enum": [
          "queued",
          "writing",
          "posted",
          "in_transit",
          "delivered",
          "returned"
        ],
        "example": "queued"
      },
      "CardEvent": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "type",
          "at"
        ],
        "properties": {
          "type": {
            "type": "string",
            "example": "card.shipped"
          },
          "at": {
            "type": "string",
            "format": "date-time",
            "example": "2026-04-18T14:00:00Z"
          }
        }
      },
      "CardCreateRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "to"
        ],
        "anyOf": [
          {
            "required": [
              "template"
            ]
          },
          {
            "required": [
              "message"
            ]
          }
        ],
        "properties": {
          "to": {
            "$ref": "#/components/schemas/Address"
          },
          "template": {
            "type": "string",
            "example": "welcome-premium"
          },
          "message": {
            "type": "string",
            "description": "Texto livre da carta quando não estiver usando um template.",
            "example": "Maria, obrigada por começar com a gente."
          },
          "style": {
            "type": "string",
            "example": "elegant"
          },
          "schedule_at": {
            "type": "string",
            "format": "date-time",
            "example": "2026-04-18T14:00:00Z"
          },
          "external_id": {
            "type": "string",
            "description": "ID do seu sistema para conciliação.",
            "example": "user_92811"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "example": {
              "crm_contact_id": "hs_92811",
              "journey": "onboarding"
            }
          }
        }
      },
      "Card": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "status",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "card_82x9a1"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "example": "user_92811"
          },
          "status": {
            "$ref": "#/components/schemas/CardStatus"
          },
          "tracking_code": {
            "type": [
              "string",
              "null"
            ],
            "example": "BR123456789BR"
          },
          "tracking_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "example": "https://rastreamento.exemplo/BR123456789BR"
          },
          "estimated_posting_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "example": "2026-04-18T14:00:00Z"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "example": "2026-04-17T19:42:10Z"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CardEvent"
            }
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "example": {
              "crm_contact_id": "hs_92811"
            }
          }
        }
      },
      "BatchCreateRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "cards"
        ],
        "properties": {
          "cards": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/CardCreateRequest"
            }
          },
          "schedule_at": {
            "type": "string",
            "format": "date-time",
            "example": "2026-04-19T09:00:00Z"
          }
        }
      },
      "Batch": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "batch_id",
          "status",
          "total_cards",
          "created_at"
        ],
        "properties": {
          "batch_id": {
            "type": "string",
            "example": "batch_91ad8"
          },
          "status": {
            "type": "string",
            "example": "queued"
          },
          "total_cards": {
            "type": "integer",
            "example": 1
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "example": "2026-04-17T19:42:10Z"
          }
        }
      },
      "Template": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "variables"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "welcome-premium"
          },
          "name": {
            "type": "string",
            "example": "Boas-vindas premium"
          },
          "variables": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "nome",
              "empresa",
              "plano"
            ]
          }
        }
      },
      "TemplateListResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "templates"
        ],
        "properties": {
          "templates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Template"
            }
          }
        }
      },
      "Style": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "elegant"
          },
          "name": {
            "type": "string",
            "example": "Elegante"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "example": "Traço refinado e formal."
          }
        }
      },
      "StyleListResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "styles"
        ],
        "properties": {
          "styles": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Style"
            }
          }
        }
      },
      "ErrorDetail": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "type",
          "code",
          "message",
          "request_id"
        ],
        "properties": {
          "type": {
            "type": "string",
            "example": "validation_error"
          },
          "code": {
            "type": "string",
            "example": "invalid_zip"
          },
          "message": {
            "type": "string",
            "example": "CEP inválido para o endereço informado."
          },
          "request_id": {
            "type": "string",
            "example": "req_8m5n21"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "$ref": "#/components/schemas/ErrorDetail"
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "event",
          "data",
          "occurred_at"
        ],
        "properties": {
          "event": {
            "type": "string",
            "example": "card.shipped"
          },
          "data": {
            "$ref": "#/components/schemas/Card"
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time",
            "example": "2026-04-18T14:00:00Z"
          }
        }
      }
    }
  },
  "paths": {
    "/cards": {
      "post": {
        "tags": [
          "Cards"
        ],
        "summary": "Criar uma carta individual",
        "description": "Cria uma carta a partir de um template aprovado ou de uma mensagem livre e devolve o identificador para acompanhamento posterior.",
        "operationId": "createCard",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CardCreateRequest"
              },
              "example": {
                "to": {
                  "name": "Maria Silva",
                  "address_line1": "Rua das Flores, 123",
                  "address_line2": "Apto 91",
                  "city": "São Paulo",
                  "state": "SP",
                  "zip": "01234-567",
                  "country": "BR"
                },
                "template": "welcome-premium",
                "style": "elegant",
                "message": "Maria, obrigada por começar com a gente.",
                "schedule_at": "2026-04-18T14:00:00Z",
                "external_id": "user_92811",
                "metadata": {
                  "crm_contact_id": "hs_92811",
                  "journey": "onboarding"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Carta criada com sucesso.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Card"
                },
                "example": {
                  "id": "card_82x9a1",
                  "external_id": "user_92811",
                  "status": "queued",
                  "tracking_code": null,
                  "tracking_url": null,
                  "estimated_posting_at": "2026-04-18T14:00:00Z",
                  "created_at": "2026-04-17T19:42:10Z",
                  "metadata": {
                    "crm_contact_id": "hs_92811",
                    "journey": "onboarding"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Payload inválido ou malformado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "type": "validation_error",
                    "code": "invalid_zip",
                    "message": "CEP inválido para o endereço informado.",
                    "request_id": "req_8m5n21"
                  }
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida ou revogada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "type": "authentication_error",
                    "code": "invalid_api_key",
                    "message": "A API key enviada não é válida.",
                    "request_id": "req_0n9sx1"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflito de idempotência.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "type": "idempotency_error",
                    "code": "idempotency_conflict",
                    "message": "A mesma Idempotency-Key foi usada com payload diferente.",
                    "request_id": "req_c8n21f"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Falha de validação de negócio.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "type": "validation_error",
                    "code": "invalid_zip",
                    "message": "CEP inválido para o endereço informado.",
                    "request_id": "req_8m5n21"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Limite temporário de requisições excedido.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Tempo em segundos até a próxima tentativa segura."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "type": "rate_limit_error",
                    "code": "rate_limit_exceeded",
                    "message": "Muitas requisições em sequência. Tente novamente depois.",
                    "request_id": "req_r1m1t0"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Erro interno inesperado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "type": "internal_error",
                    "code": "internal_error",
                    "message": "Falha interna no processamento da carta.",
                    "request_id": "req_50x500"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/cards/{id}": {
      "get": {
        "tags": [
          "Cards"
        ],
        "summary": "Consultar status da carta",
        "description": "Retorna o estado atual, rastreamento e linha do tempo operacional de uma carta já criada.",
        "operationId": "getCard",
        "parameters": [
          {
            "$ref": "#/components/parameters/CardId"
          }
        ],
        "responses": {
          "200": {
            "description": "Carta encontrada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Card"
                },
                "example": {
                  "id": "card_82x9a1",
                  "external_id": "user_92811",
                  "status": "posted",
                  "tracking_code": "BR123456789BR",
                  "tracking_url": "https://rastreamento.exemplo/BR123456789BR",
                  "estimated_posting_at": "2026-04-18T14:00:00Z",
                  "created_at": "2026-04-17T19:42:10Z",
                  "events": [
                    {
                      "type": "card.created",
                      "at": "2026-04-17T19:42:10Z"
                    },
                    {
                      "type": "card.written",
                      "at": "2026-04-18T11:05:00Z"
                    },
                    {
                      "type": "card.shipped",
                      "at": "2026-04-18T14:00:00Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida ou revogada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "type": "authentication_error",
                    "code": "invalid_api_key",
                    "message": "A API key enviada não é válida.",
                    "request_id": "req_0n9sx1"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Carta não encontrada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "type": "not_found_error",
                    "code": "card_not_found",
                    "message": "Nenhuma carta encontrada para o ID informado.",
                    "request_id": "req_404x4"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Limite temporário de requisições excedido.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Tempo em segundos até a próxima tentativa segura."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "type": "rate_limit_error",
                    "code": "rate_limit_exceeded",
                    "message": "Muitas requisições em sequência. Tente novamente depois.",
                    "request_id": "req_r1m1t0"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/batches": {
      "post": {
        "tags": [
          "Batches"
        ],
        "summary": "Criar um lote de cartas",
        "description": "Dispara múltiplas cartas em uma única operação, com agendamento opcional e idempotência no lote.",
        "operationId": "createBatch",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchCreateRequest"
              },
              "example": {
                "cards": [
                  {
                    "to": {
                      "name": "Cliente 1",
                      "address_line1": "Av. Paulista, 1000",
                      "city": "São Paulo",
                      "state": "SP",
                      "zip": "01310-100",
                      "country": "BR"
                    },
                    "template": "reativacao-q2",
                    "style": "modern",
                    "external_id": "crm_1001"
                  }
                ],
                "schedule_at": "2026-04-19T09:00:00Z"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Lote aceito e enfileirado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Batch"
                },
                "example": {
                  "batch_id": "batch_91ad8",
                  "status": "queued",
                  "total_cards": 1,
                  "created_at": "2026-04-17T19:42:10Z"
                }
              }
            }
          },
          "400": {
            "description": "Payload inválido ou malformado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "type": "validation_error",
                    "code": "invalid_zip",
                    "message": "CEP inválido para o endereço informado.",
                    "request_id": "req_8m5n21"
                  }
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida ou revogada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "type": "authentication_error",
                    "code": "invalid_api_key",
                    "message": "A API key enviada não é válida.",
                    "request_id": "req_0n9sx1"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflito de idempotência.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "type": "idempotency_error",
                    "code": "idempotency_conflict",
                    "message": "A mesma Idempotency-Key foi usada com payload diferente.",
                    "request_id": "req_c8n21f"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Falha de validação de negócio.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "type": "validation_error",
                    "code": "invalid_zip",
                    "message": "CEP inválido para o endereço informado.",
                    "request_id": "req_8m5n21"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/templates": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "summary": "Listar templates aprovados",
        "description": "Retorna os templates aprovados para a conta e as variáveis que cada um espera receber.",
        "operationId": "listTemplates",
        "responses": {
          "200": {
            "description": "Catálogo de templates disponível.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateListResponse"
                },
                "example": {
                  "templates": [
                    {
                      "id": "welcome-premium",
                      "name": "Boas-vindas premium",
                      "variables": [
                        "nome",
                        "empresa",
                        "plano"
                      ]
                    },
                    {
                      "id": "pos-venda-premium",
                      "name": "Pós-venda premium",
                      "variables": [
                        "nome",
                        "produto",
                        "data_compra"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida ou revogada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "type": "authentication_error",
                    "code": "invalid_api_key",
                    "message": "A API key enviada não é válida.",
                    "request_id": "req_0n9sx1"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/styles": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "summary": "Listar estilos de caligrafia",
        "description": "Retorna os estilos liberados para a conta, para preencher interfaces internas ou validar preferências antes do envio.",
        "operationId": "listStyles",
        "responses": {
          "200": {
            "description": "Catálogo de estilos disponível.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StyleListResponse"
                },
                "example": {
                  "styles": [
                    {
                      "id": "elegant",
                      "name": "Elegante",
                      "description": "Traço refinado e formal."
                    },
                    {
                      "id": "modern",
                      "name": "Moderna",
                      "description": "Mais limpa e contemporânea."
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida ou revogada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "type": "authentication_error",
                    "code": "invalid_api_key",
                    "message": "A API key enviada não é válida.",
                    "request_id": "req_0n9sx1"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "cardStatusChanged": {
      "post": {
        "summary": "Evento de mudança de status da carta",
        "description": "Webhook enviado sempre que a carta muda de estado operacional relevante.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              },
              "example": {
                "event": "card.shipped",
                "data": {
                  "id": "card_82x9a1",
                  "external_id": "user_92811",
                  "status": "posted",
                  "created_at": "2026-04-17T19:42:10Z",
                  "tracking_code": "BR123456789BR",
                  "tracking_url": "https://rastreamento.exemplo/BR123456789BR",
                  "metadata": {
                    "crm_contact_id": "hs_92811"
                  }
                },
                "occurred_at": "2026-04-18T14:00:00Z"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "X-SuaCarta-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Assinatura HMAC SHA-256 do payload bruto."
          },
          {
            "name": "X-SuaCarta-Timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Unix timestamp usado para proteger contra replay."
          },
          {
            "name": "X-SuaCarta-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Nome do evento enviado no payload."
          }
        ],
        "responses": {
          "200": {
            "description": "Evento recebido com sucesso."
          }
        }
      }
    }
  }
}