Agent Coordination
Register agents, acquire contact leases, and run consensus votes for multi-agent coordination.
When multiple agents operate on the same tenant, the coordination API prevents conflicts. Agents register themselves, maintain liveness via heartbeats, acquire exclusive leases on contacts, and run consensus votes for shared decisions.
Register an agent
Run once at startup to register your agent with the tenant. If an agent with the same name already exists, it is updated (upsert).
molted agents register --agent-name "outreach-agent" --agent-role outboundOptions
| Flag | Type | Required | Description |
|---|---|---|---|
--agent-name | string | Yes | Agent name. Must be unique per tenant. |
--agent-role | string | No | Agent role (e.g., outbound, inbound, classifier). Defaults to general. |
POST https://api.molted.email/v1/agent/registercurl -X POST https://api.molted.email/v1/agent/register \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tenantId": "tenant_abc123",
"agentName": "outreach-agent",
"agentRole": "outbound"
}'| Field | Type | Required | Description |
|---|---|---|---|
tenantId | string | Yes | Your tenant identifier. |
agentName | string | Yes | Agent name. Must be unique per tenant. |
agentRole | string | No | Agent role. Defaults to general. |
Response
{
"id": "agent_a1b2c3d4e5f6",
"tenantId": "tenant_abc123",
"agentName": "outreach-agent",
"agentRole": "outbound",
"lastHeartbeat": "2026-03-01T12:00:00Z",
"registeredAt": "2026-03-01T12:00:00Z"
}Heartbeat
Agents must send a heartbeat at least every 5 minutes to remain active. Agents that miss the heartbeat window are considered inactive and lose their leases.
molted agents heartbeatNo flags needed -- the CLI resolves your agent identity from your auth token.
POST https://api.molted.email/v1/agent/coordination/heartbeatcurl -X POST https://api.molted.email/v1/agent/coordination/heartbeat \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tenantId": "tenant_abc123",
"agentName": "outreach-agent"
}'| Field | Type | Required | Description |
|---|---|---|---|
tenantId | string | Yes | Your tenant identifier. |
agentId | string | No | Agent ID. Either agentId or agentName is required. |
agentName | string | No | Agent name (resolved to ID server-side). |
Response
{
"ok": true
}Contact leases
Leases give an agent exclusive access to a contact. While a lease is held, other agents' send requests for that contact are blocked with reason lease_conflict.
Acquire a lease
molted agents lease --agent-name outreach-agent \
--contact-email user@example.com --intent outbound_sales --duration 30Options
| Flag | Type | Required | Description |
|---|---|---|---|
--agent-id | string | No | Agent ID. Either --agent-id or --agent-name is required. |
--agent-name | string | No | Agent name (resolved to ID server-side). |
--contact-email | string | Yes | Contact email to lease. |
--intent | string | No | Lease intent (e.g., outbound_sales). Defaults to general. |
--duration | number | No | Lease duration in minutes. Defaults to 30. |
POST https://api.molted.email/v1/agent/coordination/leasecurl -X POST https://api.molted.email/v1/agent/coordination/lease \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tenantId": "tenant_abc123",
"agentName": "outreach-agent",
"contactEmail": "user@example.com",
"intent": "outbound_sales",
"durationMinutes": 30
}'| Field | Type | Required | Description |
|---|---|---|---|
tenantId | string | Yes | Your tenant identifier. |
agentId | string | No | Agent ID. Either agentId or agentName is required. |
agentName | string | No | Agent name (resolved to ID server-side). |
contactEmail | string | Yes | Contact email to lease. |
intent | string | No | Lease intent. Defaults to general. |
durationMinutes | number | No | Lease duration in minutes. Defaults to 30. |
Response
{
"id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"tenantId": "tenant_abc123",
"agentId": "agent_a1b2c3d4e5f6",
"contactEmail": "user@example.com",
"intent": "outbound_sales",
"expiresAt": "2026-03-01T12:30:00Z",
"createdAt": "2026-03-01T12:00:00Z"
}If another agent already holds the lease, the response includes the conflict:
{
"conflict": true,
"leaseHolder": {
"agentId": "agent_f6e5d4c3b2a1",
"agentName": "other-agent",
"expiresAt": "2026-03-01T12:30:00Z"
}
}Release a lease
molted agents release-lease LEASE_IDDELETE https://api.molted.email/v1/agent/coordination/lease/:idcurl -X DELETE "https://api.molted.email/v1/agent/coordination/lease/LEASE_ID?tenantId=tenant_abc123" \
-H "Authorization: Bearer YOUR_API_KEY"Response
{
"released": true
}List active leases
molted agents leasesGET https://api.molted.email/v1/agent/coordination/leasescurl "https://api.molted.email/v1/agent/coordination/leases?tenantId=tenant_abc123" \
-H "Authorization: Bearer YOUR_API_KEY"[
{
"id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"tenantId": "tenant_abc123",
"agentId": "agent_a1b2c3d4e5f6",
"contactEmail": "user@example.com",
"intent": "outbound_sales",
"expiresAt": "2026-03-01T12:30:00Z",
"createdAt": "2026-03-01T12:00:00Z"
}
]Consensus voting
When agents need to make a shared decision, they can create a consensus request. Active agents (those with a heartbeat within the last 5 minutes) are eligible to vote. The request is decided when a majority of active agents vote the same way.
Create a consensus request
molted agents consensus create --agent-name outreach-agent \
--action "pause outreach" --contact-email ceo@bigcorp.com \
--reason "Fatigue scores are high" --timeout 5Options
| Flag | Type | Required | Description |
|---|---|---|---|
--agent-id | string | No | Requesting agent ID. Either --agent-id or --agent-name is required. |
--agent-name | string | No | Requesting agent name (resolved to ID server-side). |
--action | string | Yes | The proposed action. |
--contact-email | string | Yes | Contact email the action applies to. |
--reason | string | Yes | Reason for the request. |
--timeout | number | No | Timeout in minutes. Defaults to 5. |
POST https://api.molted.email/v1/agent/coordination/consensuscurl -X POST https://api.molted.email/v1/agent/coordination/consensus \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tenantId": "tenant_abc123",
"proposerAgent": "outreach-agent",
"action": "pause outreach",
"contactEmail": "ceo@bigcorp.com",
"reason": "Fatigue scores are high",
"timeoutMinutes": 5
}'| Field | Type | Required | Description |
|---|---|---|---|
tenantId | string | Yes | Your tenant identifier. |
requestingAgentId | string | No | Requesting agent ID. Either requestingAgentId or proposerAgent is required. |
proposerAgent | string | No | Requesting agent name (resolved to ID server-side). |
action | string | Yes | The proposed action. |
contactEmail | string | Yes | Contact email the action applies to. |
reason | string | Yes | Reason for the request. |
timeoutMinutes | number | No | Timeout in minutes. Defaults to 5. |
Response
{
"id": "b4c8e7a2-3f19-4d5e-a621-9c1d8f3e7b0a",
"tenantId": "tenant_abc123",
"requestingAgentId": "agent_a1b2c3d4e5f6",
"action": "pause outreach",
"contactEmail": "ceo@bigcorp.com",
"reason": "Fatigue scores are high",
"status": "pending",
"timeoutAt": "2026-03-01T12:05:00Z",
"createdAt": "2026-03-01T12:00:00Z"
}Get a consensus request
molted agents consensus get CONSENSUS_IDGET https://api.molted.email/v1/agent/coordination/consensus/:idcurl "https://api.molted.email/v1/agent/coordination/consensus/CONSENSUS_ID?tenantId=tenant_abc123" \
-H "Authorization: Bearer YOUR_API_KEY"Returns the same shape as the create response, with the current status reflecting any votes cast since creation.
Cast a vote
molted agents consensus vote CONSENSUS_ID \
--agent-id agent_f6e5d4c3b2a1 --vote approve --reason "Agreed, signals look bad"Options
| Flag | Type | Required | Description |
|---|---|---|---|
--agent-id | string | Yes | Voting agent ID. |
--vote | string | Yes | Your vote: approve or reject. |
--reason | string | No | Reason for your vote. |
POST https://api.molted.email/v1/agent/coordination/consensus/:id/votecurl -X POST https://api.molted.email/v1/agent/coordination/consensus/CONSENSUS_ID/vote \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "agent_f6e5d4c3b2a1",
"vote": "approve",
"reason": "Agreed, signals look bad"
}'| Field | Type | Required | Description |
|---|---|---|---|
agentId | string | Yes | Voting agent ID. |
vote | string | Yes | Your vote: approve or reject. |
reason | string | No | Reason for your vote. |
Vote response
{
"id": "e7f3a1b2-8c4d-4e6f-b123-7d8e9f0a1b2c",
"consensusRequestId": "b4c8e7a2-3f19-4d5e-a621-9c1d8f3e7b0a",
"agentId": "agent_f6e5d4c3b2a1",
"vote": "approve",
"reason": "Agreed, signals look bad",
"consensusStatus": "approved",
"createdAt": "2026-03-01T12:01:00Z"
}The consensusStatus field reflects the current status after your vote. If your vote caused a majority, it changes to approved or rejected.
Consensus statuses
| Status | Description |
|---|---|
pending | Voting is still open. |
approved | Majority of active agents voted approve. |
rejected | Majority of active agents voted reject. |
expired | The vote timed out before reaching a decision. |
Agent config
Each agent can store configuration that controls behavior like humanizer settings.
Get config
molted agents config AGENT_IDGET https://api.molted.email/v1/agent/agents/:agentId/configcurl "https://api.molted.email/v1/agent/agents/agent_a1b2c3d4e5f6/config?tenantId=tenant_abc123" \
-H "Authorization: Bearer YOUR_API_KEY"Update config
molted agents update-config AGENT_ID \
--config '{"humanizer_enabled": true, "humanizer_style": "professional"}'PUT https://api.molted.email/v1/agent/agents/:agentId/configcurl -X PUT "https://api.molted.email/v1/agent/agents/agent_a1b2c3d4e5f6/config?tenantId=tenant_abc123" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"humanizer_enabled": true,
"humanizer_style": "professional"
}'Allowed config keys
| Key | Type | Description |
|---|---|---|
humanizer_enabled | boolean | Enable AI email humanization for this agent. |
humanizer_style | string | Humanizer writing style (e.g., professional, casual). |
humanizer_provider | string | Humanizer provider to use. |
Unrecognized keys are silently stripped.
Related
- Autonomy Levels -- control how much human approval each mailbox requires
- Sending Email -- how the policy engine evaluates send requests
- Approval Queues -- managing held sends