# Message Expiration (TTL) (/learn/queues/how-to/message-expiration)



## How Expiration Works [#how-expiration-works]

Messages with `expirationSeconds > 0` are automatically discarded if not consumed before the TTL elapses. Expired messages are removed on the next receive operation.

<Mermaid
  chart="sequenceDiagram
    participant P as Producer
    participant Q as Queue
    participant C as Consumer
    P->>Q: Send (expiration=300s)
    Note over Q: Message available for 5 minutes
    Note over Q: 5 minutes pass...
    Note over Q: Message expired
    C->>Q: Poll
    Q-->>C: No messages (expired was discarded)"
/>

*An unconsumed message is silently discarded once its TTL elapses — the next poll never sees it.*

## Set Message Expiration [#set-message-expiration]

<Tabs groupId="language" items="['Go', 'Python', 'Node.js', 'Java', 'C#', 'Kotlin', 'C++', 'Rust', 'Ruby', 'Elixir']">
  <Tab value="Go">
    ```go title="expiration.go"
    msg := kubemq.NewQueueMessage().
        SetChannel("time-sensitive-orders").
        SetBody([]byte(`{"orderId":"ORD-7001","type":"flash-sale"}`)).
        SetExpirationSeconds(300)

    result, err := client.SendQueueMessage(ctx, msg)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Sent with 5-minute TTL: id=%s, expiresAt=%d\n",
        result.MessageID, result.ExpiresAt)
    ```
  </Tab>

  <Tab value="Python">
    ```python title="expiration.py"
    result = client.send_queue_message(
        QueueMessage(
            channel="time-sensitive-orders",
            body=b'{"orderId":"ORD-7001","type":"flash-sale"}',
            expiration_in_seconds=300,
        )
    )
    print(f"Sent with 5-minute TTL: id={result.id}, expiresAt={result.expires_at}")
    ```
  </Tab>

  <Tab value="Node.js">
    ```typescript title="expiration.ts"
    const result = await client.sendQueueMessage(
      createQueueMessage({
        channel: 'time-sensitive-orders',
        body: JSON.stringify({ orderId: 'ORD-7001', type: 'flash-sale' }),
        policy: { expirationSeconds: 300 },
      }),
    );
    console.log(`Sent with 5-minute TTL: id=${result.messageId}, expiresAt=${result.expiresAt}`);
    ```
  </Tab>

  <Tab value="Java">
    ```java title="Expiration.java"
    QueueMessage msg = QueueMessage.builder()
        .channel("time-sensitive-orders")
        .body("{\"orderId\":\"ORD-7001\",\"type\":\"flash-sale\"}".getBytes())
        .expirationSeconds(300)
        .build();

    SendQueueMessageResult result = client.sendQueueMessage(msg);
    System.out.printf("Sent with 5-minute TTL: id=%s, expiresAt=%d%n",
        result.getMessageId(), result.getExpiresAt());
    ```
  </Tab>

  <Tab value="C#">
    ```csharp title="Expiration.cs"
    var result = await client.SendQueueMessageAsync(new QueueMessage
    {
        Channel = "time-sensitive-orders",
        Body = Encoding.UTF8.GetBytes("{\"orderId\":\"ORD-7001\",\"type\":\"flash-sale\"}"),
        ExpirationSeconds = 300
    });
    Console.WriteLine($"Sent with 5-minute TTL: id={result.MessageId}, expiresAt={result.ExpiresAt}");
    ```
  </Tab>

  <Tab value="Kotlin">
    ```kotlin title="Expiration.kt"
    val result = client.sendQueueMessage(QueueMessage(
        channel = "time-sensitive-orders",
        body = """{"orderId":"ORD-7001","type":"flash-sale"}""".toByteArray(),
        expirationSeconds = 300
    ))
    println("Sent with 5-minute TTL: id=${result.messageId}, expiresAt=${result.expiresAt}")
    ```
  </Tab>

  <Tab value="C++">
    ```cpp title="expiration.cpp"
    kubemq::QueueMessage msg;
    msg.channel = "time-sensitive-orders";
    msg.body = R"({"orderId":"ORD-7001","type":"flash-sale"})";
    msg.expirationSeconds = 300;

    auto result = client.sendQueueMessage(msg);
    std::cout << "Sent with 5-minute TTL: id=" << result.messageId << std::endl;
    ```
  </Tab>

  <Tab value="Rust">
    ```rust title="expiration.rs"
    let msg = QueueMessageBuilder::new()
        .channel("time-sensitive-orders")
        .body(br#"{"orderId":"ORD-7001","type":"flash-sale"}"#.to_vec())
        .expiration_seconds(300)
        .build();

    let result = client.send_queue_message(msg).await?;
    println!(
        "Sent with 5-minute TTL: id={}, expiration_at={}",
        result.message_id, result.expiration_at
    );
    ```
  </Tab>

  <Tab value="Ruby">
    ```ruby title="expiration.rb"
    policy = KubeMQ::Queues::QueueMessagePolicy.new(expiration_seconds: 300)
    msg = KubeMQ::Queues::QueueMessage.new(
      channel: 'time-sensitive-orders',
      body: '{"orderId":"ORD-7001","type":"flash-sale"}',
      policy: policy
    )

    result = client.send_queue_message(msg)
    puts "Sent with 5-minute TTL: id=#{result.id}, expiration_at=#{result.expiration_at}"
    ```
  </Tab>

  <Tab value="Elixir">
    ```elixir title="expiration.exs"
    msg = KubeMQ.QueueMessage.new(
      channel: "time-sensitive-orders",
      body: ~s({"orderId":"ORD-7001","type":"flash-sale"}),
      policy: KubeMQ.QueuePolicy.new(expiration_seconds: 300)
    )

    {:ok, result} = KubeMQ.Client.send_queue_message(client, msg)
    IO.puts("Sent with 5-minute TTL: id=#{result.message_id}, expiration_at=#{result.expiration_at}")
    ```
  </Tab>
</Tabs>

## What Happens to Expired Messages [#what-happens-to-expired-messages]

| Behavior          | Detail                                                                         |
| ----------------- | ------------------------------------------------------------------------------ |
| Expiration check  | Occurs during receive operations (not a background timer)                      |
| Expired messages  | Discarded silently — not delivered to consumers                                |
| No DLQ routing    | Expired messages are not sent to dead letter queues                            |
| Delay interaction | If delay + expiration are both set, expiration starts **after** the delay ends |

## Delay + Expiration Interaction [#delay--expiration-interaction]

| Delay | Expiration | Available At | Expires At |
| ----- | ---------- | ------------ | ---------- |
| 0     | 300s       | Immediately  | T+300s     |
| 60s   | 300s       | T+60s        | T+360s     |
| 60s   | 0          | T+60s        | Never      |

<Callout type="info">
  The maximum expiration is controlled by the server setting `MaxExpirationSeconds` (default: 43,200 seconds / 12 hours).
</Callout>

## Next Steps [#next-steps]

<Cards>
  <Card title="Delayed Messages" href="/learn/queues/tutorials/delayed-messages" description="Schedule messages for future delivery." />

  <Card title="Queue Reference" href="/learn/queues/reference" description="All policy fields and server configuration." />
</Cards>
