> ## Documentation Index
> Fetch the complete documentation index at: https://docs.malga.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Cobrança instantânea

> Primeira cobrança imediata na criação da assinatura ou ao atualizar startAt antes do primeiro ciclo no motor de assinaturas da Malga.

Quando você cria uma assinatura com `startAt` definido como a data atual, o motor de assinaturas da Malga processa a primeira cobrança imediatamente. Este comportamento especial permite que você inicie a cobrança no mesmo dia da criação da assinatura.

<Warning>
  **Importante**: O campo `startAt` deve ser informado em formato UTC (YYYY-MM-DD).
</Warning>

## Comportamento especial

Ao definir `startAt` como a data atual, o motor de assinaturas:

1. **Processa a cobrança imediatamente** após a criação da assinatura
2. **Retorna informações detalhadas** sobre o processamento na resposta da API
3. **Envia webhooks específicos** para notificar sobre o resultado da cobrança
4. **Atualiza o status** da assinatura baseado no resultado da cobrança

## Exemplo de criação com startAt sendo hoje

<CodeGroup>
  ```bash Request theme={null}
  curl --location 'https://api.malga.io/v1/subscriptions' \
  --header 'Content-Type: application/json' \
  --header 'X-Client-Id: YOUR_CLIENT_ID' \
  --header 'X-Api-Key: YOUR_API_KEY' \
  --data '{
    "name": "Assinatura Premium com Eventos",
    "merchantId": "YOUR_MERCHANT_ID",
    "customerId": "YOUR_CUSTOMER_ID",
    "referenceKey": "SUB-PREMIUM-001",
    "recurrence": {
      "interval": "monthly",
      "startAt": "2025-08-06"
    },
    "paymentMethod": {
      "type": "credit",
      "card": {
        "cardId": "YOUR_CARD_ID"
      }
    },
    "items": [
      {
        "name": "Ingresso VIP Mensal",
        "description": "Acesso VIP premium a eventos mensais",
        "amount": 29900,
        "quantity": 1,
        "sku": "VIP-EVENT-001",
        "risk": "Low",
        "categoryId": "entertainment",
        "locality": "São Paulo",
        "date": "2025-12-01",
        "type": 1,
        "genre": "Shows e Eventos",
        "tickets": {
          "quantityTicketSale": 1,
          "convenienceFeeValue": 15.5,
          "quantityFull": 1,
          "batch": 1
        },
        "location": {
          "street": "Av. Paulista",
          "number": "1000",
          "complement": "Centro de Convenções",
          "zipCode": "01310-100",
          "city": "São Paulo",
          "state": "SP",
          "country": "Brasil",
          "district": "Bela Vista",
          "reference": "Próximo ao MASP"
        }
      }
    ]
  }'
  ```

  ```json Response - Success theme={null}
  {
      "id": "subscription_id_example",
      "name": "Assinatura Premium com Eventos",
      "clientId": "YOUR_CLIENT_ID",
      "merchantId": "YOUR_MERCHANT_ID",
      "customerId": "YOUR_CUSTOMER_ID",
      "referenceKey": "SUB-PREMIUM-001",
      "currency": "BRL",
      "items": [
          {
              "name": "Ingresso VIP Mensal",
              "description": "Acesso VIP premium a eventos mensais",
              "amount": 29900,
              "quantity": 1,
              "sku": "VIP-EVENT-001",
              "risk": "Low",
              "categoryId": "entertainment",
              "locality": "São Paulo",
              "date": "2025-12-01",
              "type": 1,
              "genre": "Shows e Eventos",
              "tickets": {
                  "quantityTicketSale": 1,
                  "convenienceFeeValue": 15.5,
                  "quantityFull": 1,
                  "batch": 1
              },
              "location": {
                  "street": "Av. Paulista",
                  "number": "1000",
                  "complement": "Centro de Convenções",
                  "zipCode": "01310-100",
                  "city": "São Paulo",
                  "state": "SP",
                  "country": "Brasil",
                  "district": "Bela Vista",
                  "reference": "Próximo ao MASP"
              }
          }
      ],
      "recurrence": {
          "interval": "monthly",
          "startAt": "2025-08-05",
          "nextDueDate": "2025-09-05"
      },
      "paymentMethod": {
          "type": "credit",
          "card": {
              "cardId": "YOUR_CARD_ID"
          },
          "installments": 1
      },
      "status": "active",
      "amount": 29900,
      "liveMode": true,
      "lastCycle": {
          "id": "cycle_id_example",
          "customerId": "YOUR_CUSTOMER_ID",
          "merchantId": "YOUR_MERCHANT_ID",
          "cycle": 1,
          "attempts": 1,
          "status": "authorized",
          "isEmulated": false,
          "createdAt": "2025-08-05T17:59:45.243691Z",
          "scheduledAt": "2025-08-05",
          "executedAt": "2025-08-05",
          "paymentHistory": [
              {
                  "id": "payment_history_id_example",
                  "createdAt": "2025-08-05T17:59:45.249579Z",
                  "chargeId": "charge_id_example",
                  "attemptNumber": 1,
                  "status": "authorized",
                  "error": null
              }
          ]
      },
      "createdAt": "2025-08-05T17:59:45.219657Z",
      "updatedAt": "2025-08-05T17:59:47.02068159Z"
  }
  ```

  ```json Response - Error theme={null}
  {
      "id": "subscription_id_example",
      "name": "Assinatura Premium com Eventos",
      "clientId": "YOUR_CLIENT_ID",
      "merchantId": "YOUR_MERCHANT_ID",
      "customerId": "YOUR_CUSTOMER_ID",
      "referenceKey": "SUB-PREMIUM-001",
      "currency": "BRL",
      "items": [
          {
              "name": "Ingresso VIP Mensal",
              "description": "Acesso VIP premium a eventos mensais",
              "amount": 29900,
              "quantity": 1,
              "sku": "VIP-EVENT-001",
              "risk": "Low",
              "categoryId": "entertainment",
              "locality": "São Paulo",
              "date": "2025-12-01",
              "type": 1,
              "genre": "Shows e Eventos",
              "tickets": {
                  "quantityTicketSale": 1,
                  "convenienceFeeValue": 15.5,
                  "quantityFull": 1,
                  "batch": 1
              },
              "location": {
                  "street": "Av. Paulista",
                  "number": "1000",
                  "complement": "Centro de Convenções",
                  "zipCode": "01310-100",
                  "city": "São Paulo",
                  "state": "SP",
                  "country": "Brasil",
                  "district": "Bela Vista",
                  "reference": "Próximo ao MASP"
              }
          }
      ],
      "recurrence": {
          "interval": "monthly",
          "startAt": "2025-08-06",
          "nextDueDate": "2025-09-06"
      },
      "paymentMethod": {
          "type": "credit",
          "card": {
              "cardId": "YOUR_CARD_ID"
          },
          "installments": 1
      },
      "status": "created",
      "amount": 29900,
      "liveMode": true,
      "lastCycle": {
          "id": "cycle_id_example",
          "customerId": "YOUR_CUSTOMER_ID",
          "merchantId": "YOUR_MERCHANT_ID",
          "cycle": 1,
          "attempts": 1,
          "status": "retrying",
          "isEmulated": false,
          "createdAt": "2025-08-06T13:57:36.3877Z",
          "scheduledAt": "2025-08-06",
          "executedAt": "2025-08-06",
          "nextAttemptAt": "2025-08-07",
          "paymentHistory": [
              {
                  "id": "payment_history_id_example",
                  "createdAt": "2025-08-06T13:57:36.396306Z",
                  "chargeId": null,
                  "attemptNumber": 1,
                  "status": "failed",
                  "error": {
                      "code": 404,
                      "details": null,
                      "message": "Card not found",
                      "type": "invalid_request_error"
                  }
              }
          ]
      },
      "createdAt": "2025-08-06T13:57:36.295605Z",
      "updatedAt": "2025-08-06T13:57:36.39395571Z"
  }
  ```
</CodeGroup>

<Info>
  **Sobre o campo `lastCycle`**: O campo `lastCycle` sempre está presente nas respostas individuais de assinatura (GET, CREATE, UPDATE), mesmo quando não há cobrança instantânea. Quando não existem faturas, o campo será `null`. Nos exemplos acima, o campo contém um objeto porque há cobrança instantânea, mas em outros cenários você pode receber `"lastCycle": null`.
</Info>

## Webhooks específicos

Quando você cria uma assinatura com cobrança instantânea, você receberá webhooks específicos:

### Ordem de eventos

1. Quando a assinatura é criada: **`subscription.created`**;
2. Quando a cobrança é processada:
   * **`subscription.activated`** (em caso de sucesso);
   * **`subscription.cycle_failed`** (quando há falha no processamento da cobrança);
3. **`subscription.unpaid`** (após esgotar todas as tentativas de cobrança da fatura).

### Novo webhook: subscription.cycle\_failed

Este webhook é enviado quando uma fatura de cobrança falha após todas as retentativas:

```json theme={null}
{
  "id": "webhook_event_id_example",
  "apiVersion": "1.1",
  "object": "subscription",
  "event": "cycle_failed",
  "createdAt": "2025-08-06T13:57:36.672Z",
  "data": {
    "subscription": {
      "id": "subscription_id_example",
      "name": "Assinatura Premium com Eventos",
      "clientId": "YOUR_CLIENT_ID",
      "merchantId": "YOUR_MERCHANT_ID",
      "customerId": "YOUR_CUSTOMER_ID",
      "referenceKey": "SUB-PREMIUM-001",
      "currency": "BRL",
      "items": [
        {
          "name": "Ingresso VIP Mensal",
          "description": "Acesso VIP premium a eventos mensais",
          "amount": 29900,
          "quantity": 1,
          "sku": "VIP-EVENT-001",
          "risk": "Low",
          "categoryId": "entertainment",
          "locality": "São Paulo",
          "date": "2025-12-01",
          "type": 1,
          "genre": "Shows e Eventos",
          "tickets": {
            "quantityTicketSale": 1,
            "convenienceFeeValue": 15.5,
            "quantityFull": 1,
            "batch": 1
          },
          "location": {
            "street": "Av. Paulista",
            "number": "1000",
            "complement": "Centro de Convenções",
            "zipCode": "01310-100",
            "city": "São Paulo",
            "state": "SP",
            "country": "Brasil",
            "district": "Bela Vista",
            "reference": "Próximo ao MASP"
          }
        }
      ],
      "recurrence": {
        "interval": "monthly",
        "startAt": "2025-08-06",
        "nextDueDate": "2025-09-06"
      },
      "paymentMethod": {
        "type": "credit",
        "card": {
          "cardId": "YOUR_CARD_ID"
        },
        "installments": 1
      },
      "status": "created",
      "amount": 29900,
      "liveMode": true,
      "createdAt": "2025-08-06T13:57:36.295605Z",
      "updatedAt": "2025-08-06T13:57:36.393955Z",
      "lastCycle": {
        "id": "cycle_id_example",
        "customerId": "YOUR_CUSTOMER_ID",
        "merchantId": "YOUR_MERCHANT_ID",
        "cycle": 1,
        "attempts": 1,
        "status": "pending",
        "isEmulated": false,
        "createdAt": "2025-08-06T13:57:36.3877Z",
        "scheduledAt": "2025-08-06",
        "paymentHistory": [
          {
            "id": "payment_history_id_example",
            "createdAt": "2025-08-06T13:57:36.396306294Z",
            "chargeId": null,
            "attemptNumber": 1,
            "status": "pending",
            "error": null
          }
        ]
      }
    },
    "errorCode": 404
  }
}
```

<h2 id="startat-atualizacao-scheduler">Atualização de startAt, scheduler e validações</h2>

<Warning>
  **Atenção**: `startAt` só pode ser alterado **antes do primeiro ciclo**. Depois que o primeiro ciclo for gerado, qualquer tentativa de atualização será rejeitada com HTTP 422.
</Warning>

Se você alterar `recurrence.startAt` com `PUT /v1/subscriptions/:id` **antes do primeiro ciclo**, o comportamento segue a mesma ideia da cobrança na criação: quando a nova data de início cai na janela aceita pela plataforma, a primeira cobrança pode ser disparada **na própria resposta** da atualização, sem depender do próximo ciclo do scheduler.

Datas fora da janela aceita pela plataforma são rejeitadas com HTTP 422.

<Info>
  As mensagens de erro de validação da API são retornadas em inglês (por exemplo, em caso de falha com HTTP 422).
</Info>

### Resumo do comportamento

* **Scheduler**: para assinaturas em `created` ainda sem primeiro ciclo, a data de início da assinatura é validada com base na **data atual**.
* **Validação na atualização**: não é permitido alterar `startAt` após o primeiro ciclo. Quanto à data em `startAt`, não é aceita data anterior a **mais de um dia** em relação à data atual (mensagem HTTP 422 equivalente a `startAt cannot be more than 1 day in the past`; vide fluxograma nesta seção).
* **Mapeamento**: antes do primeiro ciclo, ao atualizar `startAt`, a **próxima data de vencimento** acompanha o novo início, para o scheduler e as regras de `next_due_date` permanecerem coerentes.
* **Cobrança imediata no update**: com `recurrence.startAt` no body e assinatura ainda elegível (`created`, sem primeiro ciclo, com a data de hoje), a primeira cobrança pode ocorrer **no mesmo request**.
* **`scheduledCancellationAt`**: na atualização da assinatura, a validação de **data** é **independente** de `startAt`: `scheduledCancellationAt` deve estar **no mínimo um dia no futuro** em relação à data atual (comparação por calendário). Detalhes em [Cancelamento agendado — Validações implementadas](/documentations/more/recurrence/engine/scheduled-cancellation#validacoes-implementadas).

### Elegibilidade do agendamento (primeiro ciclo)

```mermaid theme={null}
%%{init: {'theme': 'base', 'themeVariables': {
  'primaryColor': '#1f2937', 'primaryTextColor': '#e5e7eb',
  'primaryBorderColor': '#475569', 'lineColor': '#64748b',
  'secondaryColor': '#0f766e', 'tertiaryColor': '#7c3aed',
  'background': '#0b1220', 'mainBkg': '#111827',
  'nodeBorder': '#334155', 'clusterBkg': '#0f172a',
  'clusterBorder': '#1f2937', 'titleColor': '#e2e8f0',
  'edgeLabelBackground': '#0f172a', 'textColor': '#cbd5e1'
}}}%%
flowchart TD
    subscriptionNode([Assinatura]) --> statusCheck{status?}

    statusCheck -->|"created<br/>sem 1º ciclo"| startAtCheck{"Data de início<br/>é hoje?"}
    statusCheck -->|"created<br/>com ciclo gerado"| nextDueCreated{"Próxima cobrança<br/>é hoje?"}
    statusCheck -->|"active / unpaid"| nextDueActive{"Próxima cobrança<br/>é hoje?"}

    startAtCheck -->|"fora da janela"| excluded(["Não será processada"])
    startAtCheck -->|dentro| cancelCheck

    nextDueCreated -->|não| excluded
    nextDueCreated -->|sim| cancelCheck

    nextDueActive -->|não| excluded
    nextDueActive -->|sim| cancelCheck

    cancelCheck{"Há cancelamento<br/>agendado?"} -->|não| eligible(["Será processada"])
    cancelCheck -->|sim| excluded

    style subscriptionNode fill:#1d4ed8,stroke:#3b82f6,color:#e0f2fe
    style eligible fill:#065f46,stroke:#10b981,color:#d1fae5
    style excluded fill:#7f1d1d,stroke:#ef4444,color:#fee2e2
    style cancelCheck fill:#1f2937,stroke:#475569
    style startAtCheck fill:#312e81,stroke:#6366f1,color:#e0e7ff
```

### Fluxo do PUT com recurrence.startAt

A referência da operação está em [Atualizar assinatura](/api-reference/subscriptions/atualizar-assinatura).

```mermaid theme={null}
%%{init: {'theme': 'base', 'themeVariables': {
  'primaryColor': '#1f2937', 'primaryTextColor': '#e5e7eb',
  'primaryBorderColor': '#475569', 'lineColor': '#64748b',
  'secondaryColor': '#0f766e', 'tertiaryColor': '#92400e',
  'background': '#0b1220', 'mainBkg': '#111827',
  'nodeBorder': '#334155', 'clusterBkg': '#0f172a',
  'clusterBorder': '#1f2937', 'titleColor': '#e2e8f0',
  'edgeLabelBackground': '#0f172a', 'textColor': '#cbd5e1'
}}}%%
flowchart TD
    patch(["PUT /v1/subscriptions/:id<br/>com recurrence.startAt"]) --> cycleCheck

    subgraph validate ["Validação"]
        cycleCheck{"O primeiro ciclo<br/>já foi gerado?"}
        cycleCheck -->|Sim| err422a(["422: startAt cannot be updated<br/>after the first cycle has been generated"])
        cycleCheck -->|Não| dateCheck{"startAt é válido<br/>para atualização?"}
        dateCheck -->|Sim| ok["Válido"]
        dateCheck -->|Não| err422b(["422: startAt cannot be more than<br/>1 day in the past"])
    end

    subgraph apply ["Atualização"]
        ok --> updateSubscription["Assinatura atualizada"]
        updateSubscription --> syncCheck{"Ainda não houve<br/>a 1ª cobrança?"}
        syncCheck -->|Sim| sync["Próxima cobrança alinhada<br/>ao novo startAt"]
        syncCheck -->|Não| save
        sync --> save["Alterações salvas"]
    end

    subgraph payment ["Cobrança imediata"]
        save --> payCheck{"startAt foi enviado<br/>na atualização?"}
        payCheck -->|Não| response
        payCheck -->|Sim| eligCheck{"Assinatura criada,<br/>sem 1ª cobrança e<br/>startAt = hoje?"}
        eligCheck -->|Não| response
        eligCheck -->|Sim| charge["Geração do ciclo e<br/>processamento do pagamento"]
        charge --> response(["200 OK"])
    end

    style patch fill:#1d4ed8,stroke:#3b82f6,color:#e0f2fe
    style err422a fill:#7f1d1d,stroke:#ef4444,color:#fee2e2
    style err422b fill:#7f1d1d,stroke:#ef4444,color:#fee2e2
    style sync fill:#312e81,stroke:#6366f1,color:#e0e7ff
    style charge fill:#065f46,stroke:#10b981,color:#d1fae5
    style response fill:#065f46,stroke:#10b981,color:#d1fae5
    style validate fill:#0f172a,stroke:#1f2937
    style apply fill:#0f172a,stroke:#1f2937
    style payment fill:#0f172a,stroke:#1f2937
```

### Por que essas regras existem

Em cenários reais, uma assinatura podia permanecer em `created` com o primeiro ciclo nunca gerado quando a **data de início** era alterada retroativamente enquanto o scheduler considerava a **data atual**, ou quando a **próxima cobrança** não acompanhava a nova data de início antes do primeiro ciclo. O alinhamento da próxima data de vencimento e a **cobrança imediata no update** quando aplicável reduzem esse risco.

## Observações importantes

* **Campo `lastCycle`**: Sempre presente nas respostas individuais de subscription (GET, CREATE, UPDATE). Pode ser `null` se não houver cycles, ou um objeto completo com os detalhes do último cycle. Na cobrança instantânea, o campo conterá informações sobre o cycle processado imediatamente.
* **Status da assinatura**:
  * Sucesso: `active`
  * Erro: `created` (permanece até [retentativas esgotadas](/documentations/more/recurrence/engine/managing#regras-de-retentativas))
* **Status da fatura**:
  * Sucesso: `authorized`
  * Erro: `retrying`
* **Payment History**: Contém detalhes de cada tentativa, incluindo erros
* **Next Attempt**: Indica quando será a próxima tentativa de cobrança

## Próximos passos

<CardGroup cols={2}>
  <Card title="Gerenciar assinatura" icon="gear" href="/documentations/more/recurrence/engine/managing">
    Aprenda como gerenciar assinaturas após a criação
  </Card>

  <Card title="Configurar webhooks" icon="webhook" href="/documentations/more/recurrence/engine/webhooks">
    Configure webhooks para receber notificações em tempo real
  </Card>
</CardGroup>
