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

# Sending and consuming messages

> Send work, lease it with long polling, extend visibility, and acknowledge successful processing.

A receive is a temporary lease. The consumer must acknowledge a message after
processing it successfully. If it stops or the lease expires first, another
consumer can receive the message. Design the business operation to tolerate
that redelivery.

## Send and receive

<Tabs>
  <Tab title="Console">
    Open the queue and select **Send message** for a manual publication.
    The **Messages** tab lets you inspect messages without acquiring a lease.
    Run application consumers through the API; peeking is not processing.
  </Tab>

  <Tab title="API">
    Send a message to the queue:

    ```http theme={null}
    POST /v1/queues/{queue_id}/messages
    Content-Type: application/json
    ```

    ```json theme={null}
    {
      "body": "{\"order_id\":\"A-1001\"}",
      "attributes": {
        "event": { "type": "String", "value": "order.paid" }
      }
    }
    ```

    HTTP `201` returns a `result` containing the message ID. Receive work with:

    ```http theme={null}
    POST /v1/queues/{queue_id}/messages/receive
    Content-Type: application/json
    ```

    ```json theme={null}
    {
      "max_messages": 10,
      "wait_time_seconds": 20,
      "visibility_timeout_seconds": 60
    }
    ```

    HTTP `200` returns a `messages` array. Each leased message includes its
    body, attributes, `receipt_handle`, `receive_count`, and `visible_at`.
    Fewer than ten messages, including an empty array, is a valid result.
  </Tab>

  <Tab title="CLI">
    Save the JSON requests from the API tab, in order, as `request-1.json`, `request-2.json`. Set `QUEUE_ID` to the resource UUID. Use your configured account and region.

    ```bash theme={null}
    basaltic queue message send "$QUEUE_ID" --from-file request-1.json
    basaltic queue message receive "$QUEUE_ID" --from-file request-2.json
    ```
  </Tab>

  <Tab title="Go">
    Save the JSON requests from the API tab, in order, as `request-1.json`, `request-2.json`. Set `QUEUE_ID` to the resource UUID. Configure credentials, account, and region through the SDK environment variables.

    ```go theme={null}
    package main

    import (
    	"context"
    	"encoding/json"
    	"fmt"
    	"os"

    	basaltic "github.com/basaltic-sh/sdk-go"
    	"github.com/basaltic-sh/sdk-go/queue"
    )

    func readJSON(path string, value any) error {
    	data, err := os.ReadFile(path)
    	if err != nil {
    		return err
    	}
    	return json.Unmarshal(data, value)
    }

    func run() error {
    	ctx := context.Background()
    	cfg, err := basaltic.NewConfig(ctx)
    	if err != nil {
    		return err
    	}
    	client := queue.New(cfg)
    	var request1 queue.SendMessageRequest
    	if err := readJSON("request-1.json", &request1); err != nil {
    		return err
    	}
    	result1, err := client.SendMessage(ctx, os.Getenv("QUEUE_ID"), &request1)
    	if err != nil {
    		return err
    	}
    	fmt.Printf("%+v\n", result1)
    	var request2 queue.ReceiveRequest
    	if err := readJSON("request-2.json", &request2); err != nil {
    		return err
    	}
    	result2, err := client.ReceiveMessages(ctx, os.Getenv("QUEUE_ID"), &request2)
    	if err != nil {
    		return err
    	}
    	fmt.Printf("%+v\n", result2)
    	return nil
    }

    func main() {
    	if err := run(); err != nil {
    		fmt.Fprintln(os.Stderr, err)
    		os.Exit(1)
    	}
    }
    ```
  </Tab>
</Tabs>

## Acknowledge or extend the lease

After processing succeeds, delete with the current receipt handle:

```http theme={null}
DELETE /v1/queues/{queue_id}/messages?receipt_handle={url_encoded_receipt_handle}
```

An acknowledgement returns HTTP `204`. The receipt handle belongs to one
receive attempt. A message ID alone cannot acknowledge it. URL-encode the
handle when building a request URL and do not log it as harmless metadata.

If processing needs longer, update visibility before the current lease expires:

```http theme={null}
POST /v1/queues/{queue_id}/messages/visibility
Content-Type: application/json
```

```json theme={null}
{
  "receipt_handle": "the-current-receipt-handle",
  "visibility_timeout_seconds": 120
}
```

The timeout is measured from the visibility change. Setting it to zero makes
the message available again. Do not use an old handle after a new receive;
it cannot acknowledge or extend another consumer's lease.

## Attributes, size, and batches

Messages accept up to 20 attributes. Attribute names are 1–256 bytes and values
are at most 1,024 bytes. Types are `String`, `Number`, or `Binary`; attribute
values are strings on the wire, with standard base64 for `Binary` values.
Body and attribute sizes together must fit the
queue's `max_message_bytes` setting, up to 256 KiB.

Send, delete, and visibility batch operations accept up to ten entries.
Entries carry unique caller-chosen IDs so individual successes and failures
can be correlated. Check per-entry results and retry only the failed actions.
An HTTP success does not imply that all entries succeeded.

## Delay, long polling, and empty receives

The queue's delay applies before first delivery. Standard queue sends can
override it with `delay_seconds`; FIFO sends use the queue's delay and reject
per-message overrides. A delayed message is different from an in-flight
message: it has not been leased yet.

Use `wait_time_seconds` up to 20 for long polling, or configure the queue's
default `receive_wait_time_seconds`. Allow the HTTP client timeout to exceed
the chosen wait. An empty receive can mean no available messages, delayed or
leased messages, or a FIFO group blocked by an earlier message.

## Inspect, purge, and troubleshoot

The peek operation reads message data without changing visibility or receive
counts and requires its own permission. It is useful for diagnosis, but cannot
reserve a message for processing.

Purge deletes messages including active leases. Deleting a queue removes the
queue and its messages. Neither operation is an acknowledgement workflow.

Watch the **Metrics** tab: increasing available depth and oldest-message age
suggest consumers are falling behind; repeated receives suggest failures or
leases that are too short. Inspect `receive_count` and your consumer logs, then
configure a [dead-letter queue](/queue/fifo-and-dead-letter-queues) for poison
messages. Missing metric samples are observation gaps, not empty queues.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.