Skip to main content

Coral Cloud API

Coral Cloud is a managed platform that lets you orchestrate agents on-demand via a convenient HTTP API.

Getting keys

To get started, you need to get an API key. You can find your API keys here

Primer on Coral

Coral lets you orchestrate agents. Agent types are called registryAgents. These are the agents available to create in a session.

Sessions

In Coral, agents don’t exist outside sessions. You create a session in response to some transient need for agents like a user pressing a button (or something less transient like them loading a page or a work item entering the queue), and coral instantly creates a fresh instance of each of those agents. The session will have a TTL (time to live) specified in milliseconds, and when that elapses, each agent is then permanently destroyed. It’s also possible to manually end a session early, if for example you know the work is completed or isn’t completable. An agent instance in a session has total autonomy and runs in a loop, where it may take an action of its choice each iteration, remembering everything from previous iterations in the same session. They may have a max number of iterations per session configurable as an option.

Agent Communication

Coral agents are given tools to be able to collaborate with each other. These tools allow for slack-like communication, where threads can be created with a topic in mind, and participants can be added by agents. The send message tool they are given includes a ‘mentions’ parameter, allowing other agents to be explicitly mentioned by name. Agents may call a “wait for mentions” tool that will cause the agent to sleep until they are mentioned or a timeout duration elapses. Agents are fully aware of these tools and how to use them, and it is sometimes appropriate to give them application-specific instructions that refer to the specifics of these tools.

Groups

Agents in a session are made passively aware of all of the agents that share a group of them. Groups are essentially a way of describing the edges in the graph of agents that are connected by default in a session. An agent may be a member of many groups. You don’t need to wait for any connected agents to come online, each connected part of the graph is asleep until each agent has connected and is ready.

Custom tools / Agent-Application communication

On top of the default set of tools given to each agent, additional custom tools can be specified per agent in a session. Custom tools are the primary mechanism for getting output from agents. Unlike traditional API calls that return results directly, Coral agents run asynchronously. To receive results, you define custom tools that agents call to send data back to your application via webhooks. For example, an agent might call a submit_report tool when it completes its analysis, or a request_approval tool when it needs human input. Custom tools can also be used to let agents query your application for information they need during execution. More information about this can be found here

Best practices

Team pattern

Coral uniquely enables a sort of agent team pattern where the developer does not need to explicitly plan out how each agent will interact. This allows agents to be built with a focus on attending to certain types of responsibilities, without needing to consider the exact flow of how they’re intended to be used. This also allows applications to think in more flexible terms: these are the capabilities that need to be met; these are the agents who will likely need to work together. The agents’ own intelligence can be relied on for working around otherwise flow-breaking events, edge cases, or potential micro and macro optimizations. This is particularly well suited for cases where a large amount of responsibility or information needs to be attended to that would usually overwhelm a single agent or be impractical to build a workflow around; agents can decide themselves which context is relevant to share with each other, and form, redefine, and distribute tasks among each other in a more dynamic manner.

Workflow pattern

More workflow/algorithmic style patterns are fully supported in Coral. These patterns are particularly appropriate when there are common sub-problems that are known in advance that contain little or no subjectivity. There are several known suitable mechanisms for achieving these sorts of patterns with Coral:
  • Creating 1 session and, using options to pass in instructions to agents recommending a workflow for them
  • Using custom tools to enforce a sort of checklist
  • Creating several sessions in sequence, with some information passed between them via agent options

API Reference

Keep in mind that some response fields are omitted for brevity, so remember to allow for extra fields in any deserialization code. Most of Coral’s local functionality is offered via Coral Cloud. You can see a full reference of this by running the coral server locally and navigating to http://localhost:5555/ui/docs

Creating a session

API reference for creating a session. (For now, just use ‘default’ as the namespace) Endpoint: POST /api/v1/sessions/{namespace}

Minimal Example

fetch('https://api.coralcloud.ai/api/v1/sessions/default', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_API_KEY'
  },
  body: JSON.stringify({
    agentGraphRequest: {
      agents: [
        {
          id: {
            name: 'natural-tone-agent',
            version: '1.0.0',
            registrySourceId: { type: 'local' }
          },
          name: 'writer',
          provider: {
            type: 'local',
            runtime: 'docker'
          }
        }
      ],
      customTools: {}  // Required, even if empty
    }
  })
})
Response:
{
  "namespace": "default",
  "sessionId": "session-abc123"
}

Required Fields

{
  id: {
    name: 'agent-name',           // Registry agent name
    version: '1.0.0',              // Agent version
    registrySourceId: {
      type: 'local' | 'marketplace' | 'linked'
      // If 'linked': linkedServerId: 'server-id'
    }
  },
  name: 'unique-instance-name',    // Unique name for this agent instance
  provider: {
    type: 'local',                 // 'local', 'remote', or 'remote_request'
    runtime: 'docker'              // 'docker', 'executable', or 'function'
  }
}

Optional Fields

{
  description: 'Custom description visible to other agents',
  systemPrompt: 'Additional instructions for this agent',
  blocking: false,                 // If true, group waits for this agent to connect

  options: {
    // Agent-specific configuration (see agent schema for available options)
    EXTRA_USER_PROMPT: {
      type: 'string',
      value: 'Your custom instructions here'
    },
    MAX_ITERATIONS: {
      type: 'i32',
      value: 50
    }
  },

  customToolAccess: ['tool_name_1', 'tool_name_2'],

  plugins: [
    { type: 'close_session_tool' }  // Allows agent to close the session
  ]
}

Agent Groups

Groups define which agents are aware of each other and can communicate by default.
{
  agentGraphRequest: {
    agents: [...],
    groups: [
      ['agent1', 'agent2', 'agent3'],  // Group 1
      ['agent4', 'agent5']              // Group 2
    ]
  }
}
  • Agents in the same group can see each other and create threads together
  • An agent can be in multiple groups
  • Groups don’t start until all blocking: true agents have connected

Custom Tools Definition

Note: The customTools field is mandatory in the agentGraphRequest, even if empty (customTools: {}).
{
  agentGraphRequest: {
    agents: [...],
    groups: [...],
    customTools: {
      tool_name: {
        transport: {
          type: 'http',
          url: 'https://your-app.com/webhook',  // Coral appends /{sessionId}/{agentName}
          signatureHeader: 'X-Coral-Signature'   // Optional: header name for request signing
        },
        schema: {
          name: 'tool_name',
          description: 'What this tool does (shown to agents)',
          inputSchema: {
            type: 'object',
            properties: {
              // Define expected input parameters
              param1: { type: 'string', description: '...' },
              param2: { type: 'number', description: '...' }
            },
            required: ['param1']
          },
          outputSchema: {
            type: 'object',
            properties: {
              // Define what your webhook returns
              success: { type: 'boolean' },
              data: { type: 'string' }
            },
            required: ['success']
          }
        }
      }
    }
  }
}
To grant an agent access to a custom tool, add the tool name to the agent’s customToolAccess array.

Session Runtime Settings

{
  sessionRuntimeSettings: {
    ttl: 300000,  // Time to live in milliseconds (5 minutes)

    persistenceMode: {
      mode: 'none' | 'hold_after_exit' | 'minimum_time',
      // If 'hold_after_exit':
      duration: 60000,  // Hold for 60 seconds after session ends
      // If 'minimum_time':
      time: 120000      // Minimum 2 minutes total
    },

    webhooks: {
      sessionEnd: {
        url: 'https://your-app.com/api/session-complete'
      }
    }
  }
}

Example: Getting output from agents using custom tools

This example shows how to create a session with a custom tool that allows an agent to submit a report back to your application. This is the standard pattern for retrieving results from Coral agents.

Client-side: Creating the session

// Store session-to-user mappings
const sessionUserMap = new Map();

const response = await fetch('https://api.coralcloud.ai/api/v1/sessions/default', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_API_KEY'
  },
  body: JSON.stringify({
    agentGraphRequest: {
      agents: [
        {
          id: {
            name: 'natural-tone-agent',
            version: '1.0.0',
            registrySourceId: { type: 'local' }
          },
          name: 'report-writer',
          provider: {
            type: 'local',
            runtime: 'docker'
          },
          customToolAccess: ['submit_report'],
          options: {
            EXTRA_USER_PROMPT: {
              type: 'string',
              value: 'Analyze Q4 sales data and create a summary report'
            }
          }
        }
        // ... other agents
      ],
      groups: [
        ['report-writer']
      ],
      customTools: {
        submit_report: {
          transport: {
            type: 'http',
            url: 'https://your-app.com/api/coral-webhook',
            signatureHeader: 'X-Coral-Signature'
          },
          schema: {
            name: 'submit_report',
            description: 'Submit a completed report to the application',
            inputSchema: {
              type: 'object',
              properties: {
                title: {
                  type: 'string',
                  description: 'The title of the report'
                },
                content: {
                  type: 'string',
                  description: 'The report content in markdown format'
                }
              },
              required: ['title', 'content']
            },
            outputSchema: {
              type: 'object',
              properties: {
                success: {
                  type: 'boolean'
                },
                reportId: {
                  type: 'string'
                }
              },
              required: ['success']
            }
          }
        }
      }
    },
    sessionRuntimeSettings: {
      ttl: 300000
    }
  })
});

const { sessionId } = await response.json();

// Map the session to the current user/tenant
sessionUserMap.set(sessionId, currentUserId);

Server-side: Handling the custom tool webhook (Deno)

When the agent calls the submit_report tool, Coral will make an HTTP POST request to your webhook URL with /{sessionId}/{agentId} appended to the end. Important: Make sure you reliably map session IDs back to users/tenants to prevent mixing up user data.
import { serve } from "https://deno.land/std@0.190.0/http/server.ts";

// Store session-to-user mappings (use a database in production)
const sessionUserMap = new Map<string, string>();

serve(async (req) => {
  const url = new URL(req.url);
  const pathParts = url.pathname.split('/').filter(Boolean);

  // Extract sessionId and agentId from URL
  // e.g., /api/coral-webhook/{sessionId}/{agentId}
  const sessionId = pathParts[pathParts.length - 2];
  const agentId = pathParts[pathParts.length - 1];

  // Map sessionId to your user/tenant
  const userId = sessionUserMap.get(sessionId);

  if (!userId) {
    return new Response(JSON.stringify({
      success: false,
      error: 'Invalid session'
    }), {
      status: 400,
      headers: { 'Content-Type': 'application/json' }
    });
  }

  // Parse the tool input from the request body
  const { title, content } = await req.json();

  // Handle the report submission
  // ... your application logic here ...

  // Return the response matching your outputSchema
  return new Response(JSON.stringify({
    success: true,
    reportId: 'report-123'
  }), {
    headers: { 'Content-Type': 'application/json' }
  });
});

List all sessions

Endpoint: GET /api/v1/sessions Returns all sessions across all namespaces.

Example:

fetch('https://api.coralcloud.ai/api/v1/sessions', {
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
})
Response:
[
  {
    "namespace": "default",
    "sessions": [
      {
        "sessionId": "session-abc123",
        "closing": false
      },
      {
        "sessionId": "session-def456",
        "closing": true
      }
    ]
  }
]

List sessions in namespace

Endpoint: GET /api/v1/sessions/{namespace} Returns all sessions in a specific namespace.

Example:

fetch('https://api.coralcloud.ai/api/v1/sessions/default', {
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
})
Response:
[
  {
    "sessionId": "session-abc123",
    "closing": false
  },
  {
    "sessionId": "session-def456",
    "closing": true
  }
]

Get session state

Endpoint: GET /api/v1/sessions/{namespace}/{sessionId} Returns the complete current state of a session including all agents, threads, and messages.

Example:

fetch('https://api.coralcloud.ai/api/v1/sessions/default/session-abc123', {
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
})
Response:
{
  "id": "session-abc123",
  "namespace": "default",
  "timestamp": 1704067200000,
  "closing": false,
  "agents": [
    {
      "name": "writer",
      "registryAgentIdentifier": {
        "name": "natural-tone-agent",
        "version": "1.0.0",
        "registrySourceId": {
          "type": "local"
        }
      },
      "isConnected": true,
      "isWaiting": false,
      "description": "An agent that writes in natural tone",
      "links": ["researcher"]
    }
  ],
  "threads": [
    {
      "id": "thread-123",
      "name": "Research Discussion",
      "creatorName": "researcher",
      "participants": ["researcher", "writer"],
      "state": {
        "state": "open"
      },
      "messages": [
        {
          "id": "msg-1",
          "threadId": "thread-123",
          "senderName": "researcher",
          "text": "Here are my findings on AI developments",
          "timestamp": 1704067200000,
          "mentionNames": ["writer"]
        }
      ]
    }
  ]
}

Inspecting agent status

The session state response includes an agents array with the current status of each agent. This is useful for monitoring agent health and debugging.

Agent State Fields

FieldTypeDescription
namestringThe unique instance name given to this agent in the session
registryAgentIdentifierobjectReference to the agent’s registry entry (name, version, source)
isConnectedbooleantrue after the agent process launched and connected to Coral’s MCP server
isWaitingbooleantrue when the agent is waiting for a message from another agent
descriptionstringThe agent’s description, visible to other agents in the session
linksstring[]List of other agent names this agent is aware of (from groups)

Example: Polling for agent readiness

async function waitForAgentsReady(namespace, sessionId, timeoutMs = 30000) {
  const startTime = Date.now();

  while (Date.now() - startTime < timeoutMs) {
    const response = await fetch(
      `https://api.coralcloud.ai/api/v1/sessions/${namespace}/${sessionId}`,
      { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
    );
    const state = await response.json();

    const allConnected = state.agents.every(agent => agent.isConnected);
    if (allConnected) {
      return state.agents;
    }

    await new Promise(resolve => setTimeout(resolve, 1000));
  }

  throw new Error('Timeout waiting for agents to connect');
}

Example: Checking if agents are idle

async function areAgentsIdle(namespace, sessionId) {
  const response = await fetch(
    `https://api.coralcloud.ai/api/v1/sessions/${namespace}/${sessionId}`,
    { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
  );
  const state = await response.json();

  // All agents are idle if they're all waiting for messages
  return state.agents.every(agent => agent.isWaiting);
}

Viewing thread contents

The session state response includes a threads array containing all communication threads and their messages. This enables you to observe agent collaboration in real-time.

Thread Fields

FieldTypeDescription
idstringUnique thread identifier
namestringThread topic/name
creatorNamestringName of the agent that created the thread
participantsstring[]List of agent names participating in the thread
stateobjectEither { "state": "open" } or { "state": "closed", "summary": "..." }
messagesarrayList of messages in the thread

Message Fields

FieldTypeDescription
idstringUnique message identifier
threadIdstringID of the thread this message belongs to
senderNamestringName of the agent that sent the message
textstringMessage content
timestampnumberUnix timestamp (milliseconds)
mentionNamesstring[]List of agent names mentioned in this message

Example: Getting all messages from a session

async function getSessionMessages(namespace, sessionId) {
  const response = await fetch(
    `https://api.coralcloud.ai/api/v1/sessions/${namespace}/${sessionId}`,
    { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
  );
  const state = await response.json();

  // Flatten all messages from all threads, sorted by timestamp
  const allMessages = state.threads
    .flatMap(thread => thread.messages.map(msg => ({
      ...msg,
      threadName: thread.name
    })))
    .sort((a, b) => a.timestamp - b.timestamp);

  return allMessages;
}

Example: Streaming thread updates (polling)

async function* streamThreadUpdates(namespace, sessionId, pollIntervalMs = 2000) {
  let lastMessageId = null;

  while (true) {
    const response = await fetch(
      `https://api.coralcloud.ai/api/v1/sessions/${namespace}/${sessionId}`,
      { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
    );

    if (!response.ok) {
      if (response.status === 404) break; // Session closed
      throw new Error(`Failed to fetch session: ${response.status}`);
    }

    const state = await response.json();

    // Find new messages
    for (const thread of state.threads) {
      for (const message of thread.messages) {
        if (lastMessageId === null || message.timestamp > lastMessageId) {
          yield { thread, message };
          lastMessageId = message.timestamp;
        }
      }
    }

    if (state.closing) break;

    await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
  }
}

// Usage
for await (const { thread, message } of streamThreadUpdates('default', 'session-abc123')) {
  console.log(`[${thread.name}] ${message.senderName}: ${message.text}`);
}

Example: Finding threads by participant

async function getThreadsWithAgent(namespace, sessionId, agentName) {
  const response = await fetch(
    `https://api.coralcloud.ai/api/v1/sessions/${namespace}/${sessionId}`,
    { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
  );
  const state = await response.json();

  return state.threads.filter(thread =>
    thread.participants.includes(agentName)
  );
}

Close a session

Endpoint: DELETE /api/v1/sessions/{namespace}/{sessionId} Closes an active session, cancelling all running agents.

Example:

fetch('https://api.coralcloud.ai/api/v1/sessions/default/session-abc123', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
})
Response: 200 OK (empty body)

List agents

Endpoint: GET /api/v1/registry Lists all agent types available in the registry.

Example:

fetch('https://api.coralcloud.ai/api/v1/registry', {
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
})
Response:
[
  {
    "identifier": {
      "type": "local"
    },
    "timestamp": 1767109139139,
    "agents": [
      {
        "name": "living-knowledge-agent",
        "versions": ["0.1.0"]
      },
      {
        "name": "natural-tone-agent",
        "versions": ["1.0.0"]
      }
    ]
  }
]

Inspect an agent

Endpoint: GET /api/v1/registry/{source}/{agentName}/{version} Returns detailed information about a specific agent including its options, capabilities, and configuration schema. Sources:
  • local - Agents provided by Coral Cloud
  • marketplace - Community marketplace agents
  • linked/{serverName} - Agents from a linked server

Example:

fetch('https://api.coralcloud.ai/api/v1/registry/local/natural-tone-agent/1.0.0', {
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
})
Response:
{
  "registryAgent": {
    "info": {
      "description": "An agent that communicates in natural, conversational tone",
      "capabilities": ["resources"],
      "identifier": {
        "name": "natural-tone-agent",
        "version": "1.0.0",
        "registrySourceId": {
          "type": "local"
        }
      }
    },
    "runtimes": {
      "docker": {
        "image": "coral/natural-tone-agent:1.0.0",
        "command": ["python", "agent.py"]
      }
    },
    "options": {
      "EXTRA_USER_PROMPT": {
        "type": "string",
        "required": false,
        "description": "Additional instructions or context for the agent",
        "display": {
          "label": "Extra Instructions",
          "description": "Custom instructions to guide the agent's behavior",
          "multiline": true
        },
        "transport": "env"
      },
      "MAX_ITERATIONS": {
        "type": "i32",
        "required": false,
        "description": "Maximum number of iterations before the agent stops",
        "default": 50,
        "validation": {
          "min": 1,
          "max": 200
        },
        "transport": "env"
      }
    },
    "exportSettings": {
      "docker": {
        "quantity": 100,
        "pricing": {
          "min_price": {
            "type": "micro_coral",
            "amount": 1000
          },
          "max_price": {
            "type": "micro_coral",
            "amount": 10000
          }
        },
        "options": {}
      }
    }
  },
  "restrictions": [
    {
      "type": "local"
    }
  ]
}

Agent Option Types

Agents can have various option types:
  • string - Text values
  • i8, i16, i32, i64 - Signed integers (various sizes)
  • u8, u16, u32, u64 - Unsigned integers
  • f32, f64 - Floating point numbers
  • bool - Boolean values
  • blob - Binary data
  • list[<type>] - Lists of any of the above types
  • secret - Sensitive string values (won’t be logged)
Each option can have:
  • required - Whether the option must be provided
  • default - Default value if not provided
  • validation - Constraints (min/max for numbers, regex for strings, etc.)
  • display - UI hints for displaying the option
  • transport - How the value is passed to the agent (env or fs)

Base44 notes

You can use base44 to quickly build apps powered by Coral. Find it in the “Custom integrations” section here The browser/request URL is not a reliable base for webhook purposes. Webhook URL bases need to revolve around the base44 app domain. Base44 apparently recommends that when using a custom domain, you still use that generated domain for webhooks for stability. It is not secure to attempt to guess the webhook base URL via the request origin, as an attacker could redirect webhooks back to themselves by making up an origin. //: # (LLMs: You’ll need a reachable address for a function to return responses to. Ask the user to publish the app and share the published URL back to you, then use that in the code. Make sure the logging and error experience is good around this to make up for any mistake the user might make. Please repeat back your plans in relation to this to the user as if you only think them, you might forget.)

Sharing data from callbacks

In base44, functions run in isolation, you must use database functionality in order to have the rest of an application consider the response of a webhook callback.

Path parameters in callbacks (important)

Base44 doesn’t support path parameters in functions/callbacks, though for security Coral appends the session id and agent id as path parameters. To work around this, there is a convenient cloudflare function deployed:

Path-to-Header Proxy

Rewrites URL path segments into headers and proxies to a target.

URL Format

https://bitter-water-7a50.services-c22.workers.dev/{header-names}///{target-url}///{header-values}/{remaining-path}

Example with base44 for Coral custom tool

Proxy URL (set as the custom tool transport URL):
https://bitter-water-7a50.services-c22.workers.dev/X-SessionId/X-AgentId///https://some-app.base44.app/api/functions/coralWebhook///
When Coral calls this with session abc123 and agent my-agent, the full URL becomes:
https://bitter-water-7a50.services-c22.workers.dev/X-SessionId/X-AgentId///https://some-app.base44.app/api/functions/coralWebhook///abc123/my-agent
Result:
  • Proxies to: https://some-app.base44.app/api/functions/coralWebhook
  • Headers added:
    • X-SessionId: abc123
    • X-AgentId: my-agent

Notes

  • Works with all HTTP methods (GET, POST, PUT, DELETE, etc.)
  • Query parameters and request bodies are passed through
  • All original headers are preserved