Tip: How Does the REST Entrypoint Work in Solace Agent Mesh?

Tip: How Does the REST Entrypoint Work in Solace Agent Mesh?

Solace Agent Mesh is an event-driven framework for building distributed ecosystems of collaborative AI agents, powered by the Solace Event Mesh for enterprise-grade agent orchestration. Agents do the thinking. But to reach them programmatically from your applications, scripts, or CI/CD pipelines, you need an API. That’s what the REST Entrypoint provides.

The REST Entrypoint is a plugin that extends the base gateway framework to expose your agent mesh over HTTP. It gives you a clean async API: submit a task, get a task ID, poll for results, and download any files the agents produce. It is built on FastAPI and includes Swagger documentation out of the box.

In this post, we’ll walk through how the REST Entrypoint works, how to install and configure it, the API it exposes, and the key concepts you need to know when integrating with it.


How It Works

The REST Entrypoint runs an HTTP server (FastAPI + Uvicorn) that accepts task submissions and translates them into A2A protocol messages for the agent mesh. The flow is:

  1. Your application sends a POST request with a prompt and an optional file
  2. The Entrypoint translates the request into an A2A task and publishes it to the Solace event broker
  3. Agents process the task and publish results back
  4. Your application polls for the result using the task ID
  5. Once complete, you retrieve the response and any artifacts the agents created

The Entrypoint itself makes no LLM calls. It is a transport layer between your HTTP client and the agent mesh.


Installation

Install the REST Entrypoint plugin with a single command:

sam plugin add my-http-rest --plugin sam-rest-gateway

This creates a configuration file at configs/gateways/my-http-rest.yaml. Before starting, edit the config for your environment.

Important: Authentication is disabled by default in the scaffolded configuration. This is convenient for local development, but make sure you enable authentication before deploying to production (see the Authentication section below).

If you are running the gateway against a local Solace broker, also change the REST API port. The default port (8080) conflicts with the local broker, and the gateway will fail to start with address already in use. Set the port in your .env file:

REST_API_PORT=5050

If you are connecting to a cloud broker, the default port 8080 is fine since there is no local port conflict.

Then start it:

sam run configs/gateways/my-http-rest.yaml

API Reference

The REST Entrypoint exposes two API versions. The v2 API is the recommended path. The v1 API is deprecated and blocks until the task completes or times out.

v2 Endpoints (Recommended)

Method Path Purpose
POST /api/v2/tasks Submit a task
GET /api/v2/tasks/{taskId} Poll for result
GET /api/v2/artifacts/ List session artifacts
GET /api/v2/artifacts/{filename} Download latest artifact version
GET /api/v2/artifacts/{filename}/versions List artifact version numbers
GET /api/v2/artifacts/{filename}/versions/{version} Download specific version

Other Endpoints

Method Path Purpose
GET /health Health check (no auth required)
GET /api/v2/docs Swagger UI for v2 API
GET /api/v1/docs Swagger UI for v1 API (legacy)
POST /api/v1/invoke Synchronous task submission (deprecated)

Submitting a Task

Submit a task using multipart/form-data with two required fields: agent_name and prompt. You can optionally attach files.

The examples below assume enforce_authentication: false (dev mode), so no token is needed. If you have authentication enabled, add -H "Authorization: Bearer ${SAM_TOKEN}" to each request (see the Authentication section for how to obtain a token).

Basic request:

curl -s -X POST "http://localhost:5050/api/v2/tasks" \
    -F "agent_name=OrchestratorAgent" \
    -F "prompt=What is event-driven architecture?"

Response (202 Accepted):

{
  "taskId": "abc123-def456"
}

With a file attachment:

curl -s -X POST "http://localhost:5050/api/v2/tasks" \
    -F "agent_name=OrchestratorAgent" \
    -F "prompt=Summarize the attached document" \
    -F "files=@/path/to/document.pdf"

The Entrypoint saves uploaded files to the artifact service and enriches the prompt with file metadata before forwarding to agents.


Polling for Results

After submitting a task, poll the task endpoint until the result is ready:

curl -s -X GET "http://localhost:5050/api/v2/tasks/${TASK_ID}"
  • 202 Accepted: The task is still processing. Poll again.
  • 200 OK: The task is complete. The response contains the agent’s output.

Completed response example:

{
  "id": "task-9f7d5f465f5a4f1ca799e8e5ecb35a43",
  "sessionId": "rest-session-36b36eeb69b04da7b67708f90e5512dc",
  "contextId": "rest-session-36b36eeb69b04da7b67708f90e5512dc",
  "status": {
    "state": "completed",
    "message": {
      "role": "agent",
      "parts": [
        { "type": "text", "text": "The agent's response..." }
      ]
    },
    "timestamp": "2025-07-03T16:59:37.486480"
  },
  "artifacts": [],
  "metadata": { "agent_name": "OrchestratorAgent" }
}

Important: Results are consume-once. After the first successful 200 response, the result is deleted from the cache. Polling again returns 202. Design your client to store the response on first retrieval. If your client does not poll within 10 minutes, the result expires and is evicted.


Working with Artifacts

When agents create files (CSV reports, images, documents), they are stored as artifacts scoped to the session. Use the contextId from the poll response to retrieve them.

List artifacts in a session:

curl -s -X GET "http://localhost:5050/api/v2/artifacts/?session_id=${CONTEXT_ID}"

Download an artifact:

curl -s -X GET "http://localhost:5050/api/v2/artifacts/report.csv?session_id=${CONTEXT_ID}" \
    -o "report.csv"

Artifacts are versioned. If an agent overwrites a file, previous versions remain accessible via the /versions endpoints.


Full Workflow Example

Here is a complete workflow: submit a task, poll for the result, and download any artifacts the agent creates. This example assumes dev mode (enforce_authentication: false). If authentication is enabled, add -H "Authorization: Bearer ${SAM_TOKEN}" to each curl command (see the Authentication section for how to obtain a token).

Tip: Save this as a script file (e.g., test-rest.sh) and run it with bash test-rest.sh. Pasting multi-line scripts directly into the terminal can cause parsing errors.

export SAM_REST_ENTRY="http://localhost:5050"

# 1. Submit task
RESPONSE=$(curl -s -X POST "${SAM_REST_ENTRY}/api/v2/tasks" -F "agent_name=OrchestratorAgent" -F "prompt=Create a CSV with 5 sample products")
TASK_ID=$(echo $RESPONSE | jq -r '.taskId')
echo "Task ID: ${TASK_ID}"

# 2. Poll until complete
while true; do
    HTTP_CODE=$(curl -s -o /tmp/poll_result.json -w "%{http_code}" -X GET "${SAM_REST_ENTRY}/api/v2/tasks/${TASK_ID}")
    if [ "$HTTP_CODE" = "200" ]; then
        echo "Task complete!"
        break
    fi
    echo "Still processing... polling again in 2 seconds"
    sleep 2
done

# 3. Show the task result
echo "Result:"
cat /tmp/poll_result.json | jq .

# 4. Get context ID and list artifacts
CONTEXT_ID=$(cat /tmp/poll_result.json | jq -r '.contextId')
echo "Context ID: ${CONTEXT_ID}"
echo "Artifacts:"
curl -s -X GET "${SAM_REST_ENTRY}/api/v2/artifacts/?session_id=${CONTEXT_ID}" | jq .

# 5. Download first artifact (filename is determined by the agent)
FILENAME=$(curl -s -X GET "${SAM_REST_ENTRY}/api/v2/artifacts/?session_id=${CONTEXT_ID}" | jq -r '.[0].filename')
if [ "$FILENAME" != "null" ] && [ -n "$FILENAME" ]; then
    curl -s -X GET "${SAM_REST_ENTRY}/api/v2/artifacts/${FILENAME}?session_id=${CONTEXT_ID}" -o "${FILENAME}"
    echo "Downloaded ${FILENAME}"
else
    echo "No artifacts to download"
fi

Configuration

Here is a typical configuration with the key options:

shared_config:
  - broker_connection: &broker_connection
      broker_url: ${SOLACE_BROKER_URL, ws://localhost:8008}
      broker_username: ${SOLACE_BROKER_USERNAME, default}
      broker_password: ${SOLACE_BROKER_PASSWORD, default}
      broker_vpn: ${SOLACE_BROKER_VPN, default}

  - services:
      artifact_service: &default_artifact_service
        type: "filesystem"
        base_path: "/tmp/samv2"
        artifact_scope: namespace

apps:
  - name: my_rest_gateway_app
    app_module: sam_rest_gateway.app
    broker:
      <<: *broker_connection

    app_config:
      namespace: "${NAMESPACE}"
      gateway_id: my-rest-gw-01
      artifact_service: *default_artifact_service

      rest_api_server_host: ${REST_API_HOST, 0.0.0.0}
      rest_api_server_port: ${REST_API_PORT, 5050}

      enforce_authentication: false
      default_user_identity: "sam_dev_user"

      default_agent_name: "OrchestratorAgent"
      system_purpose: >
        The system is an AI assistant with agentic capabilities.
      response_format: >
        Responses should be clear and concise.

Configuration Options

Option Default Description
rest_api_server_host 127.0.0.1 Host address for the HTTP server
rest_api_server_port 8080 HTTP port (change to avoid conflicts, e.g., 5050)
rest_api_https_port 1943 HTTPS port (when SSL certs are provided)
ssl_keyfile / ssl_certfile - Paths to SSL certificate files for HTTPS
enforce_authentication true Require bearer token authentication
external_auth_service_url - URL of external auth service (required when auth is enforced)
default_user_identity - Fallback user ID when auth is disabled
sync_mode_timeout_seconds 60 Timeout for v1 synchronous calls
gateway_id auto-generated Unique identifier for this Entrypoint instance
system_purpose - System prompt context sent to agents
response_format - Instructions for formatting agent responses

Important: The gateway_id must be unique across all Entrypoint instances connected to the same broker. If two instances share a gateway_id, responses will be routed unpredictably.


Authentication

The REST Entrypoint has three configuration options that control authentication: enforce_authentication, default_user_identity, and force_user_identity.

enforce_authentication

This is the main switch. It determines whether the gateway requires a bearer token on every request.

When true (production):

  • Every request must include an Authorization: Bearer <token> header
  • The gateway calls your external auth service to validate the token and retrieve the user’s identity (email)
  • Requests without a valid token are rejected with 401 Unauthorized

When false (development):

  • No token is required on any request
  • The gateway uses default_user_identity to determine who the user is
  • A security warning is logged at startup

default_user_identity

This is the fallback user ID used when authentication is disabled. It tells the gateway “treat all requests as if they came from this user.”

  • If enforce_authentication: false and default_user_identity: "sam_dev_user", every request is treated as coming from sam_dev_user
  • If enforce_authentication: false and default_user_identity is missing or empty, the gateway has no user identity to assign, so it rejects the request with 401 Unauthorized

Tip: Setting default_user_identity to sam_dev_user specifically is useful because that matches the WebUI’s default user. This means you can see your REST tasks in the WebUI’s Activities tab without any extra configuration. Note that the WebUI is available in the Enterprise edition only.

force_user_identity

This overrides everything, including authenticated users:

  • If set, all requests are treated as this user regardless of token or default_user_identity
  • Purely a dev/debug tool for testing permission scopes or impersonating a specific user

Authentication: Development Setup

The scaffolded configuration already has authentication disabled with a default user set:

app_config:
  enforce_authentication: false
  default_user_identity: "sam_dev_user"

No token is needed. Submit requests directly:

# Submit a task (no auth header required)
curl -s -X POST "http://localhost:5050/api/v2/tasks" \
    -F "agent_name=OrchestratorAgent" \
    -F "prompt=Hello"

# Poll for result
curl -s -X GET "http://localhost:5050/api/v2/tasks/${TASK_ID}"

# List artifacts
curl -s -X GET "http://localhost:5050/api/v2/artifacts/?session_id=${CONTEXT_ID}"

Important: If you see 401 Unauthorized with enforce_authentication: false, check that default_user_identity is set in your config. A missing or empty value causes the gateway to reject all requests.

Note: When enforce_authentication is false, the gateway ignores Authorization headers entirely. Even if you pass a bearer token, it is not validated. The gateway always uses default_user_identity regardless. To test actual token validation, you need enforce_authentication: true with a running external auth service.


Authentication: Production Setup

For production, enable authentication and configure an external auth service:

app_config:
  enforce_authentication: true
  external_auth_service_url: ${EXTERNAL_AUTH_SERVICE_URL, https://your-auth-service.com}
  external_auth_service_provider: "azure"  # or your identity provider

How It Works

  1. Your client sends an Authorization: Bearer <token> header with each request
  2. The gateway validates the token by calling POST {external_auth_service_url}/is_token_valid with the bearer token
  3. If valid, it retrieves the user’s identity by calling GET {external_auth_service_url}/user_info?provider=<provider>
  4. The user’s email is extracted and forwarded to agents as part of the task context

Where Does the Token Come From?

The REST Entrypoint does not issue tokens. The token comes from your identity provider (Azure AD, Okta, Auth0, etc.) through your standard OAuth/OIDC flow.

If you are running the Agent Mesh WebUI alongside the REST Entrypoint with the same auth service, you can grab a valid token from the WebUI’s browser console:

// Open browser dev tools on the WebUI and run:
console.log(window.localStorage.access_token)

You can also obtain a token programmatically using your identity provider’s token endpoint. For example, with Azure AD:

# Azure AD client credentials flow (example)
curl -s -X POST "https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token" \
    -F "client_id={your-client-id}" \
    -F "client_secret={your-client-secret}" \
    -F "scope={your-scope}" \
    -F "grant_type=client_credentials"

The response includes an access_token you can use as your bearer token.

Refreshing Tokens

If your token expires, the REST Entrypoint supports token refresh via the auth service:

curl -s -X POST "${EXTERNAL_AUTH_SERVICE_URL}/refresh_token" \
    -H "Content-Type: application/json" \
    -d '{"provider": "azure", "refresh_token": "${REFRESH_TOKEN}"}'

The response includes new access_token and refresh_token values.

Prerequisites for Testing with Authentication

To test authentication, you need:

  1. enforce_authentication: true in your gateway config
  2. A running external auth service at the configured external_auth_service_url
  3. A valid bearer token from your identity provider

Without a running auth service, the gateway will return 503 Authentication service is unavailable on every request. If you are doing local development without an auth service, use the development setup (enforce_authentication: false) instead.

Testing with Authentication Enabled

# 1. Get your token (from WebUI console or your identity provider)
export SAM_TOKEN="<your-bearer-token>"

# 2. Submit a task
RESPONSE=$(curl -s -X POST "http://localhost:5050/api/v2/tasks" \
    -H "Authorization: Bearer ${SAM_TOKEN}" \
    -F "agent_name=OrchestratorAgent" \
    -F "prompt=What agents are available?")
TASK_ID=$(echo $RESPONSE | jq -r '.taskId')
echo "Task ID: ${TASK_ID}"

# 3. Poll for result
curl -s -X GET "http://localhost:5050/api/v2/tasks/${TASK_ID}" \
    -H "Authorization: Bearer ${SAM_TOKEN}"

# 4. List artifacts
CONTEXT_ID="<contextId from poll response>"
curl -s -X GET "http://localhost:5050/api/v2/artifacts/?session_id=${CONTEXT_ID}" \
    -H "Authorization: Bearer ${SAM_TOKEN}"

Error Responses (only when enforce_authentication: true)

These errors only occur when authentication is enforced. With enforce_authentication: false, the gateway accepts all requests regardless of the token.

Scenario HTTP Status Message
No Authorization header 401 "Bearer token not provided."
Invalid or expired token 401 "Invalid or expired token."
Auth service unreachable 503 "Authentication service is unavailable."
User email not in auth response 401 "User email not provided by auth provider"
User not found in identity service 403 "User not authorized for this application"

Session Management

Each REST API request creates a fresh session with a unique ID (rest-session-{uuid}). There is no persistent conversation state between requests.

This means:

  • Every request starts a new conversation with the agent
  • Artifacts are scoped to the session they were created in
  • The contextId in the poll response is the session ID you need for artifact retrieval

If you need multi-turn conversations, consider using the WebUI Entrypoint (which supports persistent sessions) or building a client that manages session continuity at the application level.


REST vs. Webhook: Which One to Use?

Both are HTTP-based Entrypoints, but they serve different purposes:

REST Webhook
Direction Bidirectional: submit tasks and retrieve results Unidirectional: ingest events, no result return
Endpoints Fixed API paths (/api/v2/tasks, /api/v2/artifacts) Dynamic, user-defined paths (/hooks/data-feed)
Payload Multipart form data (agent_name, prompt, files) Flexible: JSON, YAML, text, binary per endpoint
Auth Centralized bearer token for all endpoints Per-endpoint: token, basic, or none
Use case Apps, scripts, CI/CD needing agent responses Incoming webhooks from GitHub, Stripe, IoT

Use the REST Entrypoint when your application needs to send a request and get a response. Use the Webhook Entrypoint when external systems push events that should trigger agent processing without waiting for a result.


Last Words of Wisdom

  1. Handle consume-once results. The poll endpoint returns the result exactly once, then deletes it. Your client should store the response on first retrieval.

  2. Poll with backoff. Start with a short interval (1-2 seconds) and increase if the task takes longer. Results expire after 10 minutes if not retrieved, so make sure your client polls within that window.

  3. Retrieve artifacts before the session expires. Artifacts are scoped to the session. If you need the files, download them as part of your workflow.

  4. Use Swagger for exploration. Visit /api/v2/docs to explore the API interactively. No authentication required for the docs endpoint.

  5. Scope your gateway_id. Every Entrypoint instance needs a unique ID. Use environment variables (gateway_id: ${MY_REST_GW_ID, rest-gw-01}) so you can run multiple instances safely.

  6. Watch your ports. The REST Entrypoint defaults to port 8080, which conflicts with the local Solace broker. Use a free port like 5050 in your config.

  7. Use consistent identity across gateways. Set default_user_identity: "sam_dev_user" in your REST gateway config to match the WebUI’s default. This ensures consistent user identity when running multiple gateways against the same broker.


For more information about Solace Agent Mesh, visit the repository:

An event-driven framework designed to build and orchestrate multi-agent AI systems. It enables seamless integration of AI agents with real-world data sources and systems, facilitating complex, multi-step workflows.


1 Like