openapi: 3.1.0
info:
  title: SocialMediaPRO API
  version: '2026-09-23'
  description: |
    API pública versionada (/v1) para revendedores SMM e sellers do marketplace.

    - Autenticação: `Authorization: Bearer smp_live_…` (ou `X-Api-Key`). Chaves com escopos, rotação e IP allowlist opcional.
    - Limite por chave (cabeçalhos `RateLimit-Limit/Remaining/Reset`; 429 com `Retry-After`).
    - **Idempotência**: `POST /v1/orders` e `/v1/orders/batch` exigem `Idempotency-Key`. Repetir a chamada devolve o MESMO pedido; reutilizar a chave com outro corpo → 409 `IDEMPOTENCY_KEY_REUSE`.
    - Erros: `{ "error": { "code", "message", "details" }, "request_id" }` — integre pelo `code`.
    - Dinheiro sempre como string decimal (nunca float), com `currency` ISO 4217.
    - Webhooks assinados: `Webhook-Id`, `Webhook-Timestamp`, `Webhook-Signature: t=<unix>,v1=<hex HMAC-SHA256(segredo, "<t>.<corpo>")>`. Rejeite assinaturas com mais de 5 minutos e deduplique por `Webhook-Id`.
    - Compatibilidade: `POST /api/v2` implementa o padrão "SMM API v2" (key + action) para painéis de terceiros.
servers:
  - url: https://YOUR-DOMAIN
security:
  - bearer: []
components:
  securitySchemes:
    bearer:
      type: http
      scheme: bearer
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema: { type: string, minLength: 8, maxLength: 120 }
    Id:
      name: id
      in: path
      required: true
      schema: { type: string }
  schemas:
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              enum: [VALIDATION_ERROR, UNAUTHORIZED, INSUFFICIENT_FUNDS, FORBIDDEN, NOT_FOUND, CONFLICT, INVALID_TRANSITION, DUPLICATE_ORDER, IDEMPOTENCY_KEY_REUSE, ALREADY_RESERVED, POLICY_BLOCKED, MARGIN_GUARD, UNPROCESSABLE, NO_ROUTE, RATE_LIMITED, PROVIDER_ERROR, NOT_CONFIGURED, INTERNAL]
            message: { type: string }
            details: { type: object }
        request_id: { type: string }
    Platform:
      type: object
      properties: { code: { type: string }, label: { type: string }, count: { type: integer } }
    Service:
      type: object
      properties:
        id: { type: string }
        slug: { type: string }
        name: { type: string }
        description: { type: string }
        platform: { type: string }
        action: { type: string }
        type: { type: string, description: 'DEFAULT, PACKAGE, CUSTOM_COMMENTS, MENTIONS, POLL, …' }
        quality: { type: string, enum: [ECONOMY, STANDARD, PREMIUM] }
        audience_country: { type: string }
        refill_days: { type: integer }
        cancel: { type: boolean }
        min: { type: integer }
        max: { type: integer }
        pricing_unit: { type: string, enum: [per_1000, per_order] }
        rate: { type: string, description: 'Preço por unidade de venda na moeda da conta' }
        currency: { type: string }
        start_time: { type: string }
        speed: { type: string }
        fields:
          type: array
          items:
            type: object
            properties: { name: { type: string }, type: { type: string, enum: [url, integer, string, text_lines] }, required: { type: boolean }, min: { type: integer }, max: { type: integer } }
    OrderCreate:
      type: object
      required: [service]
      properties:
        service: { type: string, description: 'id do serviço (catálogo)' }
        link: { type: string, format: uri }
        quantity: { type: integer }
        comments: { type: string, description: 'CUSTOM_COMMENTS: um por linha (a quantidade é o nº de linhas)' }
        usernames: { type: string }
        answer_number: { type: integer }
        runs: { type: integer, description: 'drip-feed: 2-100' }
        interval: { type: integer, description: 'drip-feed: minutos 1-1440' }
        coupon: { type: string }
        project_id: { type: string }
    Order:
      type: object
      properties:
        id: { type: string, example: SMP-7K2Q9XHT }
        status: { type: string, enum: [paid, routing, submitting, accepted, pending, in_progress, partial, completed, refill_requested, refill_in_progress, refilled, cancel_requested, canceled, failed, refunded, disputed, manual_review] }
        service: { type: string }
        link: { type: string }
        quantity: { type: integer }
        charge: { type: string }
        refunded: { type: string }
        currency: { type: string }
        start_count: { type: [integer, 'null'] }
        remains: { type: [integer, 'null'] }
        delivered: { type: [integer, 'null'] }
        refill_until: { type: [string, 'null'], format: date-time }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    Balance:
      type: object
      properties:
        data:
          type: array
          items:
            type: object
            properties: { currency: { type: string }, available: { type: string }, cash: { type: string }, bonus: { type: string }, reserved: { type: string }, frozen: { type: boolean } }
    AssetCreate:
      type: object
      required: [assetType, platform, title, description, transferMethod, askingPrice, currency]
      properties:
        externalRef: { type: string }
        assetType: { type: string, enum: [social_account, channel, page, group, playlist, newsletter, forum, domain, website, ecommerce, saas, mobile_app, paid_community, content_library, brand_ip, subscriber_base] }
        platform: { type: string }
        handle: { type: string }
        publicUrl: { type: string, format: uri }
        title: { type: string, minLength: 8 }
        description: { type: string, minLength: 30 }
        niche: { type: string }
        country: { type: string, minLength: 2, maxLength: 2 }
        audienceCountries: { type: array, items: { type: string } }
        followers: { type: integer }
        averageViews: { type: integer }
        engagementRate: { type: number }
        accountAgeMonths: { type: integer }
        monetized: { type: boolean }
        monthlyRevenue: { type: number }
        monthlyProfit: { type: number }
        originalEmailAvailable: { type: boolean }
        transferMethod: { type: string, enum: [OWNERSHIP_TRANSFER, ROLE_TRANSFER, CHANNEL_OWNERSHIP, DOMAIN_TRANSFER, HOSTING_TRANSFER, REPO_TRANSFER, APP_STORE_TRANSFER, PLATFORM_ACCOUNT_TRANSFER, DATA_EXPORT, IP_ASSIGNMENT] }
        askingPrice: { type: number }
        currency: { type: string }
        offersEnabled: { type: boolean }
    WebhookEvent:
      type: object
      properties:
        id: { type: string }
        type: { type: string, enum: [order.created, order.status_changed, order.partial, order.completed, order.refunded, order.refill_updated, deposit.confirmed, offer.created, offer.updated, reservation.created, transaction.updated, payout.updated, listing.updated, dispute.updated] }
        created_at: { type: string, format: date-time }
        data: { type: object }
  responses:
    Error:
      description: Erro
      content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } }
paths:
  /v1/catalog/platforms:
    get:
      summary: Plataformas com serviços publicados
      tags: [SMM]
      responses: { '200': { description: OK, content: { application/json: { schema: { type: object, properties: { data: { type: array, items: { $ref: '#/components/schemas/Platform' } } } } } } }, default: { $ref: '#/components/responses/Error' } }
  /v1/catalog/services:
    get:
      summary: Serviços com preço na moeda e nível da conta (escopo catalog:read)
      tags: [SMM]
      parameters: [{ name: platform, in: query, schema: { type: string } }]
      responses: { '200': { description: OK, content: { application/json: { schema: { type: object, properties: { data: { type: array, items: { $ref: '#/components/schemas/Service' } }, currency: { type: string } } } } } }, default: { $ref: '#/components/responses/Error' } }
  /v1/catalog/services/{id}:
    get:
      summary: Serviço com schema de campos
      tags: [SMM]
      parameters: [{ $ref: '#/components/parameters/Id' }]
      responses: { '200': { description: OK, content: { application/json: { schema: { $ref: '#/components/schemas/Service' } } } }, default: { $ref: '#/components/responses/Error' } }
  /v1/orders/preview:
    post:
      summary: Valida e cota SEM criar nem cobrar (orders:write)
      tags: [SMM]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/OrderCreate' } } } }
      responses: { '200': { description: Cotação, content: { application/json: { schema: { type: object, properties: { charge: { type: string }, currency: { type: string }, quantity: { type: integer }, breakdown: { type: array, items: { type: object } } } } } } }, default: { $ref: '#/components/responses/Error' } }
  /v1/orders:
    post:
      summary: Cria pedido (debita a carteira; roteia ao melhor fornecedor)
      tags: [SMM]
      parameters: [{ $ref: '#/components/parameters/IdempotencyKey' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/OrderCreate' } } } }
      responses: { '201': { description: Pedido, content: { application/json: { schema: { $ref: '#/components/schemas/Order' } } } }, default: { $ref: '#/components/responses/Error' } }
    get:
      summary: Lista pedidos (mais recentes primeiro, paginação por cursor)
      tags: [SMM]
      parameters: [{ name: limit, in: query, schema: { type: integer, maximum: 200 } }, { name: starting_after, in: query, schema: { type: string } }]
      responses: { '200': { description: OK, content: { application/json: { schema: { type: object, properties: { data: { type: array, items: { $ref: '#/components/schemas/Order' } }, has_more: { type: boolean }, next_cursor: { type: [string, 'null'] } } } } } } }
  /v1/orders/batch:
    post:
      summary: Até 100 pedidos; cada item com resultado próprio
      tags: [SMM]
      parameters: [{ $ref: '#/components/parameters/IdempotencyKey' }]
      requestBody: { required: true, content: { application/json: { schema: { type: object, properties: { orders: { type: array, maxItems: 100, items: { $ref: '#/components/schemas/OrderCreate' } } } } } } }
      responses: { '200': { description: Resultado por item } }
  /v1/orders/{id}:
    get:
      summary: Pedido
      tags: [SMM]
      parameters: [{ $ref: '#/components/parameters/Id' }]
      responses: { '200': { description: OK, content: { application/json: { schema: { $ref: '#/components/schemas/Order' } } } }, default: { $ref: '#/components/responses/Error' } }
  /v1/orders/{id}/refill:
    post:
      summary: Solicita refill (dentro da janela de garantia)
      tags: [SMM]
      parameters: [{ $ref: '#/components/parameters/Id' }]
      responses: { '200': { description: Refill criado }, default: { $ref: '#/components/responses/Error' } }
  /v1/orders/{id}/cancel:
    post:
      summary: Solicita cancelamento (reembolso quando o fornecedor confirmar)
      tags: [SMM]
      parameters: [{ $ref: '#/components/parameters/Id' }]
      responses: { '200': { description: Pedido, content: { application/json: { schema: { $ref: '#/components/schemas/Order' } } } }, default: { $ref: '#/components/responses/Error' } }
  /v1/wallet/balance:
    get:
      summary: Saldos por moeda (wallet:read)
      tags: [SMM]
      responses: { '200': { description: OK, content: { application/json: { schema: { $ref: '#/components/schemas/Balance' } } } } }
  /v1/assets:
    post:
      summary: Cria anúncio (rascunho; vai ao ar só após verificação de propriedade e moderação)
      tags: [Seller]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/AssetCreate' } } } }
      responses: { '201': { description: Criado }, default: { $ref: '#/components/responses/Error' } }
    get:
      summary: Anúncios do seller
      tags: [Seller]
      responses: { '200': { description: OK } }
  /v1/assets/{id}:
    patch:
      summary: Atualiza anúncio (corpo completo; If-Match com a versão para trava otimista)
      tags: [Seller]
      parameters: [{ $ref: '#/components/parameters/Id' }, { name: If-Match, in: header, schema: { type: string } }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/AssetCreate' } } } }
      responses: { '200': { description: OK }, default: { $ref: '#/components/responses/Error' } }
  /v1/assets/batch:
    post:
      summary: Importa até 1.000 anúncios (colunas do modelo CSV, casamento por external_ref)
      tags: [Seller]
      requestBody: { required: true, content: { application/json: { schema: { type: object, properties: { assets: { type: array, maxItems: 1000, items: { type: object } } } } } } }
      responses: { '200': { description: Resumo com erros por linha } }
  /v1/assets/{id}/verify:
    post:
      summary: Inicia (method) ou confere (verification_id) a verificação de propriedade
      tags: [Seller]
      parameters: [{ $ref: '#/components/parameters/Id' }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - { type: object, properties: { method: { type: string, enum: [DNS_TXT, HTML_META, YOUTUBE_API, TELEGRAM_BOT, BIO_CHALLENGE, ADMIN_INVITE, VIDEO_EVIDENCE] } } }
                - { type: object, properties: { verification_id: { type: string } } }
      responses: { '200': { description: 'Código, status e expiração' } }
  /v1/assets/{id}/availability:
    post:
      summary: Tira/volta o ativo da vitrine (não vale para ativo em negociação)
      tags: [Seller]
      parameters: [{ $ref: '#/components/parameters/Id' }]
      requestBody: { required: true, content: { application/json: { schema: { type: object, properties: { available: { type: boolean } } } } } }
      responses: { '200': { description: OK } }
  /v1/offers:
    get:
      summary: Ofertas recebidas
      tags: [Seller]
      parameters: [{ name: status, in: query, schema: { type: string } }]
      responses: { '200': { description: OK } }
  /v1/transactions/{id}/handoff:
    post:
      summary: Marca passo do playbook, adiciona item seguro (cofre, leitura única) e/ou conclui o handoff
      tags: [Seller]
      parameters: [{ $ref: '#/components/parameters/Id' }]
      requestBody: { required: true, content: { application/json: { schema: { type: object, properties: { step: { type: string }, secure_item: { type: object, properties: { label: { type: string }, content: { type: string } } }, submit: { type: boolean }, notes: { type: string } } } } } }
      responses: { '200': { description: OK } }
webhooks:
  event:
    post:
      summary: Evento assinado entregue ao seu endpoint
      parameters:
        - { name: Webhook-Id, in: header, schema: { type: string } }
        - { name: Webhook-Timestamp, in: header, schema: { type: string } }
        - { name: Webhook-Signature, in: header, schema: { type: string } }
      requestBody: { content: { application/json: { schema: { $ref: '#/components/schemas/WebhookEvent' } } } }
      responses: { '200': { description: 'Responda 2xx; retentativas em 1m, 5m, 30m, 2h, 6h, 12h, 24h' } }
