# Criar produto

POST /catalog/products

Cria um produto no catálogo da inbox. Precisa de `name` e de uma foto (`image_data`, `image_url` ou `image_urls`). A revisão do WhatsApp vem em `status_info`.

## Autenticação

```json
[
  {
    "token": []
  }
]
```

```json
{
  "token": {
    "name": "token",
    "type": "apiKey",
    "in": "header"
  }
}
```

## Corpo da requisição

```json
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/BusinessProductCreate"
      },
      "example": {
        "name": "Camisa azul",
        "price": "89900",
        "currency": "BRL",
        "retailer_id": "CAMISA-01",
        "image_urls": [
          "https://loja.com/camisa.jpg"
        ]
      }
    }
  }
}
```

## Respostas

```json
{
  "201": {
    "description": "Criado.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/BusinessProduct"
        },
        "example": {
          "id": "8123456789012345",
          "retailer_id": "CAMISA-01",
          "name": "Camisa azul",
          "description": "Algodão, tamanhos P a GG.",
          "price": "89900",
          "currency": "BRL",
          "is_hidden": false,
          "is_sanctioned": false,
          "media": {
            "images": [
              {
                "id": "1",
                "original_image_url": "https://loja.com/camisa.jpg"
              }
            ]
          },
          "status_info": {
            "status": "APPROVED",
            "can_appeal": false
          }
        }
      }
    }
  },
  "400": {
    "description": "Pedido inválido.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/OrkesioError"
        },
        "example": {
          "error": "invalid_request",
          "message": "price é obrigatório."
        }
      }
    }
  },
  "401": {
    "description": "Token inválido ou ausente.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/OrkesioError"
        },
        "example": {
          "error": "unauthorized",
          "message": "Token inválido."
        }
      }
    }
  },
  "403": {
    "description": "`not_admin`: o WhatsApp recusou por falta de permissão (a conta precisa ser comercial e dona do catálogo).",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/OrkesioError"
        },
        "example": {
          "error": "not_admin",
          "message": "O WhatsApp recusou por falta de permissão."
        }
      }
    }
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  },
  "500": {
    "$ref": "#/components/responses/InternalError"
  },
  "501": {
    "$ref": "#/components/responses/CloudUnsupported"
  },
  "503": {
    "description": "`disconnected`: a inbox está desconectada do WhatsApp. `unavailable`: o WhatsApp não respondeu agora (conexão caindo ou sem resposta); tente de novo.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/OrkesioError"
        },
        "example": {
          "error": "unavailable",
          "message": "O WhatsApp não respondeu agora; tente de novo."
        }
      }
    }
  }
}
```

**Estado:** Disponível

## Compatibilidade de canais

| Canal | Compatível | Observação |
|---|---|---|
| WhatsApp (Web) | Sim |  |
| WhatsApp (API oficial) | Não | Na API oficial, perfil e catálogo ficam no Gerenciador do WhatsApp da Meta. |
| Instagram | Não |  |
| Messenger | Não |  |
| Telegram | Não |  |

## Eventos que esta rota gera

Nenhum. Esta rota não gera evento no webhook (nem em `GET /events`): o resultado vem só na resposta.

## Antes e depois (uazapi → Orkesio)

Na uazapi: `POST /business/catalog/save`.

- Criar é `POST` (antes o `save` sem `id`); os campos são os mesmos da uazapi; responde o produto sem o envelope `response`.

## #/components/schemas/BusinessProductCreate

```json
{
  "type": "object",
  "required": [
    "name"
  ],
  "properties": {
    "name": {
      "type": "string",
      "maxLength": 256,
      "description": "Nome do produto."
    },
    "description": {
      "type": "string",
      "maxLength": 4096,
      "description": "Descrição do produto."
    },
    "currency": {
      "type": "string",
      "pattern": "^[A-Z]{3}$",
      "example": "BRL",
      "description": "Moeda do preço, em código ISO 4217 (ex.: `BRL`). Obrigatória com `price`."
    },
    "price": {
      "type": "string",
      "pattern": "^\\d{1,18}$",
      "description": "Preço em milésimos da moeda, só dígitos (19990 = R$ 19,99). Exige `currency`.",
      "example": "19990"
    },
    "sale_price": {
      "type": "string",
      "pattern": "^\\d{1,18}$",
      "description": "Preço promocional em milésimos da moeda. Exige `price`."
    },
    "url": {
      "type": "string",
      "format": "uri",
      "maxLength": 2048,
      "description": "Link HTTPS do produto no seu site."
    },
    "retailer_id": {
      "type": "string",
      "maxLength": 256,
      "description": "Código do produto no seu sistema (SKU)."
    },
    "is_hidden": {
      "type": "boolean",
      "default": false,
      "description": "`true` cria o produto escondido do catálogo."
    },
    "image_urls": {
      "type": "array",
      "minItems": 1,
      "maxItems": 10,
      "items": {
        "type": "string",
        "format": "uri"
      },
      "description": "Fotos já hospedadas no WhatsApp (os links de `media.images` de um produto), de 1 a 10. Outros endereços são recusados; para foto nova, use `image_data` ou `image_url`."
    },
    "video_urls": {
      "type": "array",
      "maxItems": 10,
      "items": {
        "type": "string",
        "format": "uri"
      },
      "description": "Vídeos já hospedados no WhatsApp (os links de `media.videos`), até 10."
    },
    "image_data": {
      "type": "string",
      "description": "Foto nova: JPEG ou PNG em base64 ou Data URL, até 16 MB. Troca as fotos do produto por esta e vale mais que `image_url`."
    },
    "image_url": {
      "type": "string",
      "format": "uri",
      "maxLength": 2048,
      "description": "Foto nova por link público HTTPS (JPEG ou PNG, até 16 MB). A api baixa e envia ao WhatsApp; troca as fotos do produto por esta."
    },
    "compliance_category": {
      "type": "string",
      "maxLength": 128,
      "description": "Categoria de conformidade, exigida pelo WhatsApp em alguns países."
    },
    "compliance_info": {
      "$ref": "#/components/schemas/BusinessComplianceInfo"
    },
    "width": {
      "type": "integer",
      "minimum": 1,
      "maximum": 1024,
      "description": "Largura, em pixels, da foto em `media.images[].request_image_url` na resposta (padrão 100)."
    },
    "height": {
      "type": "integer",
      "minimum": 1,
      "maximum": 1024,
      "description": "Altura, em pixels, da foto em `media.images[].request_image_url` na resposta (padrão 100)."
    }
  },
  "additionalProperties": false,
  "description": "Produto novo. Precisa de `name` e de uma foto (`image_data`, `image_url` ou `image_urls`)."
}
```

## #/components/schemas/BusinessComplianceInfo

```json
{
  "type": "object",
  "properties": {
    "country_code_origin": {
      "type": "string",
      "description": "País de origem (código de 2 ou 3 letras)."
    },
    "importer_name": {
      "type": "string",
      "description": "Nome do importador."
    },
    "importer_address": {
      "$ref": "#/components/schemas/BusinessAddress"
    }
  }
}
```

## #/components/schemas/BusinessAddress

```json
{
  "type": "object",
  "properties": {
    "street1": {
      "type": "string",
      "description": "Endereço (linha 1)."
    },
    "street2": {
      "type": "string",
      "description": "Complemento."
    },
    "city": {
      "type": "string",
      "description": "Cidade."
    },
    "region": {
      "type": "string",
      "description": "Estado ou região."
    },
    "postal_code": {
      "type": "string",
      "description": "CEP ou código postal."
    },
    "country_code": {
      "type": "string",
      "description": "País (código de 2 ou 3 letras)."
    }
  }
}
```

## #/components/schemas/BusinessProduct

```json
{
  "type": "object",
  "required": [
    "id",
    "name",
    "price",
    "currency",
    "is_hidden",
    "is_sanctioned",
    "media",
    "status_info"
  ],
  "properties": {
    "id": {
      "type": "string",
      "description": "Id do produto no catálogo do WhatsApp."
    },
    "retailer_id": {
      "type": "string",
      "description": "Código do produto no seu sistema (SKU), quando informado."
    },
    "belongs_to": {
      "type": "string"
    },
    "name": {
      "type": "string",
      "description": "Nome do produto."
    },
    "description": {
      "type": "string",
      "description": "Descrição do produto."
    },
    "price": {
      "type": "string",
      "description": "Valor em milésimos da moeda (19990 = R$ 19,99)."
    },
    "currency": {
      "type": "string",
      "description": "Moeda do preço, em código ISO 4217 (ex.: `BRL`)."
    },
    "url": {
      "type": "string",
      "description": "Link do produto no seu site."
    },
    "shimmed_url": {
      "type": "string",
      "description": "O `url` passado pelo redirecionador do WhatsApp, como aparece no app."
    },
    "is_hidden": {
      "type": "boolean",
      "description": "Escondido do catálogo para os clientes."
    },
    "is_sanctioned": {
      "type": "boolean"
    },
    "max_available": {
      "type": "integer"
    },
    "product_availability": {
      "type": "string"
    },
    "compliance_category": {
      "type": "string",
      "description": "Categoria de conformidade, exigida pelo WhatsApp em alguns países."
    },
    "compliance_info": {
      "type": "object",
      "properties": {
        "country_code_origin": {
          "type": "string",
          "description": "País de origem (código de 2 ou 3 letras)."
        },
        "importer_name": {
          "type": "string",
          "description": "Nome do importador."
        },
        "importer_address": {
          "type": "object",
          "properties": {
            "street1": {
              "type": "string",
              "description": "Endereço (linha 1)."
            },
            "street2": {
              "type": "string",
              "description": "Complemento."
            },
            "city": {
              "type": "string",
              "description": "Cidade."
            },
            "region": {
              "type": "string",
              "description": "Estado ou região."
            },
            "postal_code": {
              "type": "string",
              "description": "CEP ou código postal."
            },
            "country_code": {
              "type": "string",
              "description": "País (código de 2 ou 3 letras)."
            }
          },
          "description": "Endereço do importador."
        }
      },
      "description": "Dados de conformidade: país de origem e importador."
    },
    "media": {
      "type": "object",
      "properties": {
        "images": {
          "type": "array",
          "items": {
            "type": "object",
            "required": [
              "id"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "Id da foto no WhatsApp."
              },
              "original_image_url": {
                "type": "string",
                "description": "Link da foto no tamanho original."
              },
              "request_image_url": {
                "type": "string",
                "description": "Link da foto redimensionada (100×100, ou o `width`/`height` da criação e da edição)."
              }
            }
          },
          "description": "Fotos."
        },
        "videos": {
          "type": "array",
          "items": {
            "type": "object",
            "required": [
              "id"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "Id do vídeo no WhatsApp."
              },
              "original_video_url": {
                "type": "string",
                "description": "Link do vídeo."
              },
              "thumbnail_url": {
                "type": "string",
                "description": "Link da miniatura do vídeo."
              }
            }
          },
          "description": "Vídeos."
        }
      },
      "description": "Fotos e vídeos do produto."
    },
    "sale_price": {
      "type": "object",
      "required": [
        "price"
      ],
      "properties": {
        "price": {
          "type": "string",
          "description": "Valor promocional em milésimos da moeda, como `price`."
        },
        "start_date": {
          "type": "string",
          "description": "Início da promoção, como o WhatsApp manda."
        },
        "end_date": {
          "type": "string",
          "description": "Fim da promoção, como o WhatsApp manda."
        }
      },
      "description": "Preço promocional, quando há."
    },
    "status_info": {
      "type": "object",
      "properties": {
        "status": {
          "type": "string",
          "description": "Estado da revisão, como o WhatsApp manda."
        },
        "can_appeal": {
          "type": "boolean",
          "description": "Dá para recorrer da recusa."
        }
      },
      "description": "Revisão do produto pelo WhatsApp."
    },
    "variant_info": {
      "type": "object",
      "properties": {
        "listing_details": {
          "type": "object",
          "properties": {
            "description": {
              "type": "string",
              "description": "Descrição das variações."
            },
            "lowest_price": {
              "type": "string",
              "description": "Menor preço entre as variações."
            },
            "multi_price": {
              "type": "string",
              "description": "Faixa de preço das variações, como o WhatsApp manda."
            }
          },
          "description": "Resumo das variações."
        },
        "availability": {
          "type": "object",
          "additionalProperties": true,
          "description": "Disponibilidade de cada variação."
        },
        "types": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": true
          },
          "description": "Tipos de variação (ex.: tamanho) e as opções de cada um."
        },
        "variant_properties": {
          "type": "array",
          "items": {
            "type": "object",
            "required": [
              "name",
              "value"
            ],
            "properties": {
              "name": {
                "type": "string",
                "description": "Tipo da variação (ex.: `Cor`)."
              },
              "value": {
                "type": "string",
                "description": "Opção deste produto (ex.: `Azul`)."
              }
            }
          },
          "description": "As opções deste produto (ex.: cor azul)."
        }
      },
      "description": "Variações do produto (tamanho, cor...), quando há."
    }
  },
  "description": "Produto do catálogo do WhatsApp Business."
}
```

## #/components/schemas/OrkesioError

```json
{
  "type": "object",
  "properties": {
    "error": {
      "type": "string",
      "description": "Código estável do erro, em inglês e `snake_case`. Os de uso geral: `invalid_request` (400): pedido inválido: JSON malformado, campo desconhecido ou valor fora do permitido (o detalhe em `message`; o campo em `field`, quando é um só); `unauthorized` (401): `token` ou `admintoken` ausente ou inválido; `forbidden` (403): o token não pode fazer isso (no servidor de demonstração, por exemplo); `not_admin` (403): o WhatsApp recusou por falta de permissão no grupo ou na newsletter; `account_restricted` (403): o WhatsApp restringiu a conta de iniciar conversas novas (mandar para quem já conversa continua valendo; até quando, em `GET /inbox/limits`); `new_chat_limit` (403): a conta atingiu a cota do WhatsApp de conversas novas neste ciclo (veja `GET /inbox/limits`); os dois também no `error` de `OrkesioMessage` e de `message.status`, e não adianta repetir o envio; `payment_required` (403): na API oficial, a Meta recusou o envio por um problema no pagamento da conta do WhatsApp Business (erro 131042 da Meta); acerte a forma de pagamento no gerenciador da Meta, porque repetir não adianta; também no `error` de `OrkesioMessage` e de `message.status`; `whatsapp_rejected` (400): o WhatsApp recusou a mudança pedida (o campo, em `field`, quando é um só); `not_found` (404): o recurso não existe nesta inbox; `conflict` (409): o recurso já existe ou mudou (id em uso, inbox de outro canal); `inbox_not_paired` (409): a inbox ainda não conectou um número; `payload_too_large` (413): o corpo passou do limite; `canceled` e `expired`: só no `error` de `OrkesioMessage` e de `message.status` (envio cancelado antes de sair, ou que passou do prazo na fila); `rate_limited` (429): muitos pedidos; espere o `Retry-After`; `internal_error` (500): falha inesperada na api; tente de novo; `send_failed` (500): o envio falhou (o motivo em `message`); também no `error` de `OrkesioMessage` e de `message.status`; `unsupported_for_channel` (501): o canal da inbox não atende esta rota ou este tipo de mensagem; `meta_error` (502): a Meta recusou ou não respondeu; `disconnected` (503): a inbox está desconectada (sem sessão do WhatsApp, ou token do canal oficial vencido); `unavailable` (503 ou 504): o WhatsApp não respondeu a tempo, ou algo em andamento impede agora; tente de novo. Cada rota lista os códigos próprios dela (`outside_window`, `number_invalid`, `flow_invalid`...). A lista pode crescer: trate código desconhecido pelo status HTTP."
    },
    "message": {
      "type": "string",
      "description": "Descrição para pessoas (às vezes o texto do WhatsApp ou da Meta). Não compare o texto: use `error`."
    },
    "field": {
      "type": "string",
      "description": "O campo do pedido que causou o erro, quando é um só (por exemplo, o campo que o WhatsApp recusou num `PATCH`)."
    }
  },
  "required": [
    "error",
    "message"
  ],
  "additionalProperties": true,
  "description": "O objeto de erro da Orkesio, o mesmo em todo lugar: o corpo das respostas de erro e o `error` de `OrkesioMessage`, `message.status` e `message.failed`. Algumas rotas juntam campos próprios (`request_id`, `errors`...)."
}
```

## #/components/responses/RateLimited

```json
{
  "description": "`rate_limited`: passou do limite de chamadas desta inbox (ou do admin); espere o tempo do header `Retry-After` e tente de novo.",
  "headers": {
    "Retry-After": {
      "description": "Segundos até poder chamar de novo.",
      "schema": {
        "type": "integer",
        "minimum": 1
      }
    }
  },
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/OrkesioError"
      },
      "example": {
        "error": "rate_limited",
        "message": "Muitas requisições."
      }
    }
  }
}
```

## #/components/responses/InternalError

```json
{
  "description": "`internal_error`: falha inesperada na api (no banco, por exemplo); tente de novo.",
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/OrkesioError"
      },
      "example": {
        "error": "internal_error",
        "message": "Falha inesperada."
      }
    }
  }
}
```

## #/components/responses/CloudUnsupported

```json
{
  "description": "`unsupported_for_channel`: o canal da inbox não atende esta rota (rota só do WhatsApp Web numa inbox oficial, ou rota da API oficial numa inbox do WhatsApp Web). O canal está em `Inbox.channel`.",
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/OrkesioError"
      },
      "example": {
        "error": "unsupported_for_channel",
        "message": "Esta rota não está disponível no canal da inbox."
      }
    }
  }
}
```

## Guias relacionados

[Visão geral](/docs/overview) · [O que funciona em cada canal](/docs/canais) · [O que muda da uazapi](/docs/o-que-muda)
