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

# Send your first API message

> Discover a template's inputs, send to a test channel, and safely handle retries.

Use an active template, a connected test Slack workspace, and an [API key](/guides/api-keys) with `templates:read` and `messages:create`. Use a test channel in your account; see [availability](/guides/availability) for environment separation.

<Steps>
  <Step title="Configure your environment">
    ```bash theme={null}
    export LITHOBLOCKS_API_URL="https://api.lithoblocks.com"
    export LITHOBLOCKS_API_KEY="YOUR_PREVIEW_KEY"
    export LITHOBLOCKS_TEMPLATE_ID="YOUR_TEMPLATE_ID"
    ```

    Load the real key through your secret manager in a deployed application.
  </Step>

  <Step title="Discover the input shape">
    ```bash theme={null}
    curl --fail-with-body --max-time 30 \
      "$LITHOBLOCKS_API_URL/v1/templates/$LITHOBLOCKS_TEMPLATE_ID/input-schema" \
      -H "Authorization: Bearer $LITHOBLOCKS_API_KEY"
    ```

    Match the selected version's inputs. The example below assumes the [Quickstart](/quickstart) fields `user.name` and `user.role`.
  </Step>

  <Step title="Send to a test channel">
    Replace `C_TEST_CHANNEL` with a channel ID. Generate one operation ID per intended send and preserve it for retries of that send.

    ```bash theme={null}
    export LITHOBLOCKS_OPERATION_ID="welcome-alex-onboarding-001"
    curl --fail-with-body --max-time 30 \
      "$LITHOBLOCKS_API_URL/v1/templates/send" \
      -H "Authorization: Bearer $LITHOBLOCKS_API_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: $LITHOBLOCKS_OPERATION_ID" \
      --data "{
        \"template_id\": \"$LITHOBLOCKS_TEMPLATE_ID\",
        \"channel_id\": \"C_TEST_CHANNEL\",
        \"data\": { \"user\": { \"name\": \"Alex Johnson\", \"role\": \"Software Engineer\" } }
      }"
    ```
  </Step>

  <Step title="Verify the result">
    A successful response contains `success`, `channel`, and `message_ts`. Save the message coordinates and response request ID. Check Slack for the resolved name and role, then test the handbook link.
  </Step>
</Steps>

A representative success response is:

```json theme={null}
{ "success": true, "channel": "C_TEST_CHANNEL", "message_ts": "1788880000.123456" }
```

## Destination choices

Supply exactly one destination per send:

| Field                | Meaning                                         |
| -------------------- | ----------------------------------------------- |
| `channel_id`         | Slack channel ID, not a name such as `#general` |
| `recipient_slack_id` | Slack user ID for a DM                          |
| `recipient_email`    | Email resolved in the connected Slack workspace |

For `POST /v1/templates/send-bulk`, provide `destinations` with 1–100 such objects. Inspect every result, including when HTTP status is 200. Do not resend successful destinations as part of retrying a partial failure.

## Compile without sending

Call `POST /v1/templates/compile` with `template_id` and `data`, omitting the destination. Compilation spends credits and returns `compiled_message`; it does not post to Slack. To check newly authored blocks without spending message credits, use `POST /v1/templates/validate` instead.

## Handle errors and retries

A missing scope returns 403, with `code: "insufficient_scope"` and `required_scopes`. Invalid destination combinations return a validation error. Use the endpoint reference for exact response schemas.

For retries, keep the same idempotency key and identical body. A timeout or 5xx can leave an uncertain outcome; inspect logs before attempting a new operation. Never change keys simply to bypass an in-progress or uncertain result. Completed responses below 500 can expire after 24 hours, so replay protection is not permanent.

[API conventions](/api-reference/introduction) · [Logs](/guides/logs) · [Complete template example](/guides/examples)
