openapi: 3.0.3
info:
  title: 'Br Envio API v1'
  description: 'API pública de integração do Br Envio: cadastrar e atualizar contatos (e tags) via Bearer API token.'
  version: 1.0.0
servers:
  - url: 'https://api.brenvio.com.br'
tags:
  - name: 'Contact Integration'
    description: "\nUpsert de contatos para CRM e sistemas externos.\nAutenticação: `Authorization: Bearer <api_token>` (token gerado em Conta → Tokens de API).\nRate limit: 60 req/min por token. Envelope `{ success, data }` / `{ success: false, error }`."
  - name: Tags
    description: "\nCatálogo de marcadores (tags) da conta.\nAutenticação neste portal: `Authorization: Bearer <api_token>` nas rotas `/api/v1/integration/tags*`.\nRate limit: 60 req/min por token. Envelope `{ success, data }` / `{ success: false, error }`."
components:
  securitySchemes:
    default:
      type: http
      scheme: bearer
      description: 'Crie o token em **Conta → Tokens de API** no painel (`https://painel.brenvio.com.br/settings/api-tokens`). O valor em texto só aparece na criação.'
security:
  -
    default: []
paths:
  /api/v1/integration/contacts:
    post:
      summary: 'Upsert contact'
      operationId: upsertContact
      description: "Cria ou atualiza um contato pelo e-mail. Tags são anexadas por nome (criadas se não existirem).\nCampos tipados (`fields`) são upsert parcial — só os slugs enviados. Não reativa contatos\n`unsubscribed` ou `suppressed`. Respeita blocklist do cliente e suppressão global."
      parameters: []
      responses:
        200:
          description: updated
          content:
            application/json:
              schema:
                type: object
                example:
                  success: true
                  message: 'Contato atualizado via integração.'
                  data:
                    id: 1
                    email: helena@crm.test
                    created: false
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: 'Contato atualizado via integração.'
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        example: 1
                      email:
                        type: string
                        example: helena@crm.test
                      created:
                        type: boolean
                        example: false
        201:
          description: created
          content:
            application/json:
              schema:
                type: object
                example:
                  success: true
                  message: 'Contato criado via integração.'
                  data:
                    id: 1
                    email: helena@crm.test
                    name: Helena
                    status: active
                    tags:
                      -
                        id: 1
                        name: VIP
                    fields:
                      empresa: Acme
                    created: true
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: 'Contato criado via integração.'
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        example: 1
                      email:
                        type: string
                        example: helena@crm.test
                      name:
                        type: string
                        example: Helena
                      status:
                        type: string
                        example: active
                      tags:
                        type: array
                        example:
                          - id: 1
                            name: VIP
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              example: 1
                            name:
                              type: string
                              example: VIP
                      fields:
                        type: object
                        properties:
                          empresa:
                            type: string
                            example: Acme
                      created:
                        type: boolean
                        example: true
        401:
          description: unauthorized
          content:
            application/json:
              schema:
                type: object
                example:
                  success: false
                  error:
                    code: UNAUTHORIZED
                    message: 'Autenticação necessária.'
                properties:
                  success:
                    type: boolean
                    example: false
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        example: UNAUTHORIZED
                      message:
                        type: string
                        example: 'Autenticação necessária.'
        422:
          description: ''
          content:
            application/json:
              schema:
                oneOf:
                  -
                    description: blocklisted
                    type: object
                    example:
                      success: false
                      error:
                        code: CONTACT_BLOCKLISTED
                        message: 'Este e-mail ou domínio está na lista de bloqueio e não pode ser cadastrado.'
                    properties:
                      success:
                        type: boolean
                        example: false
                      error:
                        type: object
                        properties:
                          code:
                            type: string
                            example: CONTACT_BLOCKLISTED
                          message:
                            type: string
                            example: 'Este e-mail ou domínio está na lista de bloqueio e não pode ser cadastrado.'
                  -
                    description: suppressed
                    type: object
                    example:
                      success: false
                      error:
                        code: CONTACT_SUPPRESSED
                        message: 'Este e-mail ou domínio está na lista de supressão e não pode ser cadastrado.'
                    properties:
                      success:
                        type: boolean
                        example: false
                      error:
                        type: object
                        properties:
                          code:
                            type: string
                            example: CONTACT_SUPPRESSED
                          message:
                            type: string
                            example: 'Este e-mail ou domínio está na lista de supressão e não pode ser cadastrado.'
                  -
                    description: unknown_field
                    type: object
                    example:
                      success: false
                      error:
                        code: UNKNOWN_FIELD_SLUG
                        message: 'Slug de campo desconhecido.'
                    properties:
                      success:
                        type: boolean
                        example: false
                      error:
                        type: object
                        properties:
                          code:
                            type: string
                            example: UNKNOWN_FIELD_SLUG
                          message:
                            type: string
                            example: 'Slug de campo desconhecido.'
        429:
          description: rate_limited
          content:
            application/json:
              schema:
                type: object
                example:
                  success: false
                  error:
                    code: RATE_LIMIT_EXCEEDED
                    message: 'Limite de requisições excedido.'
                properties:
                  success:
                    type: boolean
                    example: false
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        example: RATE_LIMIT_EXCEEDED
                      message:
                        type: string
                        example: 'Limite de requisições excedido.'
      tags:
        - 'Contact Integration'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  description: 'E-mail do contato (lowercase).'
                  example: helena@crm.test
                name:
                  type: string
                  description: 'Nome de exibição.'
                  example: Helena
                  nullable: true
                country_code:
                  type: string
                  description: 'DDI só dígitos (default 55 se vier phone).'
                  example: '55'
                  nullable: true
                phone_number:
                  type: string
                  description: 'Telefone só dígitos.'
                  example: '11988887777'
                  nullable: true
                phone:
                  type: string
                  description: 'Alternativa: telefone em uma string (normalizado no servidor).'
                  example: '+55 11 98888-7777'
                  nullable: true
                tags:
                  type: array
                  description: 'Nomes de tags (cria se faltar). Com `tag_match=append` (default) anexa; com `replace` substitui o conjunto.'
                  example:
                    - 'Lead CRM'
                    - VIP
                  items:
                    type: string
                tag_match:
                  type: string
                  description: '`append` (default) ou `replace`.'
                  example: append
                fields:
                  type: object
                  description: 'Valores de campos tipados por slug do catálogo.'
                  example:
                    empresa: Acme
                  properties: {}
              required:
                - email
  /api/v1/integration/tags:
    get:
      summary: 'List tags'
      operationId: listTags
      description: 'Lista paginada de tags. Busca opcional por nome (`search`).'
      parameters:
        -
          in: query
          name: search
          description: 'Filtro parcial no nome (case-insensitive).'
          example: vip
          required: false
          schema:
            type: string
            description: 'Filtro parcial no nome (case-insensitive).'
            example: vip
        -
          in: query
          name: per_page
          description: 'Itens por página (1–100, default 15).'
          example: 15
          required: false
          schema:
            type: integer
            description: 'Itens por página (1–100, default 15).'
            example: 15
      responses:
        200:
          description: ok
          content:
            application/json:
              schema:
                type: object
                example:
                  success: true
                  data:
                    -
                      id: 1
                      name: VIP
                  meta:
                    current_page: 1
                    per_page: 15
                    total: 1
                    last_page: 1
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: array
                    example:
                      - id: 1
                        name: VIP
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          example: 1
                        name:
                          type: string
                          example: VIP
                  meta:
                    type: object
                    properties:
                      current_page:
                        type: integer
                        example: 1
                      per_page:
                        type: integer
                        example: 15
                      total:
                        type: integer
                        example: 1
                      last_page:
                        type: integer
                        example: 1
      tags:
        - Tags
    post:
      summary: 'Create tag'
      operationId: createTag
      description: 'Cria uma tag pelo nome. Nome duplicado (case-insensitive) → validação 422.'
      parameters: []
      responses:
        201:
          description: created
          content:
            application/json:
              schema:
                type: object
                example:
                  success: true
                  message: 'Tag criada.'
                  data:
                    id: 1
                    name: 'Lead CRM'
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: 'Tag criada.'
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        example: 1
                      name:
                        type: string
                        example: 'Lead CRM'
      tags:
        - Tags
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: 'Nome do marcador.'
                  example: 'Lead CRM'
              required:
                - name
  '/api/v1/integration/tags/{tag}':
    get:
      summary: 'Get tag'
      operationId: getTag
      description: ''
      parameters: []
      responses:
        200:
          description: ok
          content:
            application/json:
              schema:
                type: object
                example:
                  success: true
                  data:
                    id: 1
                    name: VIP
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        example: 1
                      name:
                        type: string
                        example: VIP
        404:
          description: missing
          content:
            application/json:
              schema:
                type: object
                example:
                  success: false
                  error:
                    code: NOT_FOUND
                    message: …
                properties:
                  success:
                    type: boolean
                    example: false
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        example: NOT_FOUND
                      message:
                        type: string
                        example: …
      tags:
        - Tags
    put:
      summary: 'Update tag'
      operationId: updateTag
      description: 'Renomeia a tag. Não altera vínculos com contatos (só o nome).'
      parameters: []
      responses:
        200:
          description: ok
          content:
            application/json:
              schema:
                type: object
                example:
                  success: true
                  message: 'Tag atualizada.'
                  data:
                    id: 1
                    name: 'VIP 2026'
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: 'Tag atualizada.'
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        example: 1
                      name:
                        type: string
                        example: 'VIP 2026'
      tags:
        - Tags
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: 'Novo nome.'
                  example: 'VIP 2026'
              required:
                - name
    delete:
      summary: 'Delete tag'
      operationId: deleteTag
      description: 'Remove a tag e os vínculos com contatos (cascade). Os contatos permanecem.'
      parameters: []
      responses:
        200:
          description: ok
          content:
            application/json:
              schema:
                type: object
                example:
                  success: true
                  message: 'Tag excluída.'
                  data: null
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: 'Tag excluída.'
                  data:
                    type: string
                    example: null
                    nullable: true
      tags:
        - Tags
    parameters:
      -
        in: path
        name: tag
        description: 'ID da tag.'
        example: 1
        required: true
        schema:
          type: integer
