# Delayed Messages (/sdks/java/how-to/queues/delayed-messages)



<Callout type="info" title="Which to use">
  This is the task-oriented guide for sending delayed messages — send one with a delivery delay, confirm it's hidden, then receive it once the delay expires. For the `delayInSeconds` builder option reference and its edge cases, see [Delay Policy](./delay-policy).
</Callout>

## Overview [#overview]

A **delivery delay** holds a queue message out of consumers' reach for a fixed window after it's sent — the message is accepted and persisted immediately, but invisible to pollers until the delay expires. It's the building block for scheduled work — a reminder to fire in an hour, a retry with back-off, a task queued for off-peak processing — without standing up a separate scheduler or cron service.

Set it with `.delayInSeconds(...)` on the `QueueMessage` builder before sending; the broker does the waiting. A poll against the channel before the delay elapses (via `receiveQueueMessages`) simply returns zero messages — it isn't hidden in a separate place, it's the same queue, just not yet eligible for delivery. Once the delay window passes, the next poll retrieves it normally.

**Gotchas:** the delay is set once at send time, per message, and can't be extended or shortened afterward — if you need a different wait, send a new message. A long delay still counts as an in-flight, persisted message, so it survives a broker restart, but it also occupies queue storage for the whole waiting period. Don't confuse this with a *visibility timeout* after delivery — that's a separate mechanism for redelivery on failed acknowledgment, not initial availability.

## Prerequisites [#prerequisites]

* KubeMQ server running on `localhost:50000`
* Java SDK installed (`implementation 'io.kubemq.sdk:kubemq-sdk-Java:3.1.1'` (Gradle) or Maven dependency from [Getting Started](/sdks/java))

## Code [#code]

```java title="DelayedMessagesExample.java"
package io.kubemq.example.queues;

import io.kubemq.sdk.queues.*;
import java.util.UUID;

/**
 * Delayed Messages Example
 *
 * Demonstrates sending messages with a delay before they become available for consumption.
 */
public class DelayedMessagesExample {
    private static final String ADDRESS = "localhost:50000";
    private static final String CLIENT_ID = "java-queues-delayed-messages-client";
    private static final String CHANNEL = "java-queues.delayed-messages";

    public static void main(String[] args) throws InterruptedException {
        // Create a queues client connected to the KubeMQ server
        try (QueuesClient client = QueuesClient.builder().address(ADDRESS).clientId(CLIENT_ID).build()) {
            // Create the queue channel
            client.createQueuesChannel(CHANNEL);

            // Send a message with delay (not available for consumption until delay expires)
            int delaySeconds = 3;
            client.sendQueueMessage(QueueMessage.builder()
                    .id(UUID.randomUUID().toString()).channel(CHANNEL)
                    .body(("Delayed by " + delaySeconds + "s").getBytes())
                    .delayInSeconds(delaySeconds).build());
            System.out.println("Sent message with " + delaySeconds + "s delay.");

            // Try to receive immediately (message not yet available)
            System.out.println("Trying to receive immediately...");
            QueuesPollResponse resp1 = client.receiveQueueMessages(QueuesPollRequest.builder()
                    .channel(CHANNEL).pollMaxMessages(1).pollWaitTimeoutInSeconds(1).autoAckMessages(true).build());
            System.out.println("Messages available: " + resp1.getMessages().size() + " (expected 0)");

            // Wait for the delay to expire
            System.out.println("Waiting for delay to expire...");
            Thread.sleep((delaySeconds + 1) * 1000);

            // Receive the message after delay has expired
            QueuesPollResponse resp2 = client.receiveQueueMessages(QueuesPollRequest.builder()
                    .channel(CHANNEL).pollMaxMessages(1).pollWaitTimeoutInSeconds(2).autoAckMessages(true).build());
            if (!resp2.getMessages().isEmpty()) {
                System.out.println("Received after delay: " + new String(resp2.getMessages().get(0).getBody()));
            }

            // Clean up resources
            client.deleteQueuesChannel(CHANNEL);
        }
    }
}

```

## How It Works [#how-it-works]

* `delayInSeconds(3)` on the `QueueMessage` tells the broker to hold the message for 3 seconds before making it available for consumers.
* The first poll (1-second timeout) returns empty because the delay has not expired; the second poll after `Thread.sleep((delaySeconds + 1) * 1000)` finds the message available.
* The delay is set per-message at publish time; different messages on the same channel can have different delays.

## Related [#related]

* [Delay Policy](./delay-policy) — field-level reference for `delayInSeconds`
* [Pattern overview](/learn/queues/getting-started)
* [Java SDK Reference](/sdks/java/reference/queues)
* [Send & Receive](/sdks/java/tutorials/send-receive)
* [Ack All](/sdks/java/how-to/queues/ack-all)
