MOLTED EMAIL

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).

CLI
molted agents register --agent-name "outreach-agent" --agent-role outbound

Options

FlagTypeRequiredDescription
--agent-namestringYesAgent name. Must be unique per tenant.
--agent-rolestringNoAgent role (e.g., outbound, inbound, classifier). Defaults to general.
POST https://api.molted.email/v1/agent/register
curl
curl -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"
  }'
FieldTypeRequiredDescription
tenantIdstringYesYour tenant identifier.
agentNamestringYesAgent name. Must be unique per tenant.
agentRolestringNoAgent role. Defaults to general.

Response

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.

CLI
molted agents heartbeat

No flags needed -- the CLI resolves your agent identity from your auth token.

POST https://api.molted.email/v1/agent/coordination/heartbeat
curl
curl -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"
  }'
FieldTypeRequiredDescription
tenantIdstringYesYour tenant identifier.
agentIdstringNoAgent ID. Either agentId or agentName is required.
agentNamestringNoAgent name (resolved to ID server-side).

Response

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

CLI
molted agents lease --agent-name outreach-agent \
  --contact-email user@example.com --intent outbound_sales --duration 30

Options

FlagTypeRequiredDescription
--agent-idstringNoAgent ID. Either --agent-id or --agent-name is required.
--agent-namestringNoAgent name (resolved to ID server-side).
--contact-emailstringYesContact email to lease.
--intentstringNoLease intent (e.g., outbound_sales). Defaults to general.
--durationnumberNoLease duration in minutes. Defaults to 30.
POST https://api.molted.email/v1/agent/coordination/lease
curl
curl -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
  }'
FieldTypeRequiredDescription
tenantIdstringYesYour tenant identifier.
agentIdstringNoAgent ID. Either agentId or agentName is required.
agentNamestringNoAgent name (resolved to ID server-side).
contactEmailstringYesContact email to lease.
intentstringNoLease intent. Defaults to general.
durationMinutesnumberNoLease duration in minutes. Defaults to 30.

Response

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:

Response (conflict)
{
  "conflict": true,
  "leaseHolder": {
    "agentId": "agent_f6e5d4c3b2a1",
    "agentName": "other-agent",
    "expiresAt": "2026-03-01T12:30:00Z"
  }
}

Release a lease

CLI
molted agents release-lease LEASE_ID
DELETE https://api.molted.email/v1/agent/coordination/lease/:id
curl
curl -X DELETE "https://api.molted.email/v1/agent/coordination/lease/LEASE_ID?tenantId=tenant_abc123" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

Response
{
  "released": true
}

List active leases

CLI
molted agents leases
GET https://api.molted.email/v1/agent/coordination/leases
curl
curl "https://api.molted.email/v1/agent/coordination/leases?tenantId=tenant_abc123" \
  -H "Authorization: Bearer YOUR_API_KEY"
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"
  }
]

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

CLI
molted agents consensus create --agent-name outreach-agent \
  --action "pause outreach" --contact-email ceo@bigcorp.com \
  --reason "Fatigue scores are high" --timeout 5

Options

FlagTypeRequiredDescription
--agent-idstringNoRequesting agent ID. Either --agent-id or --agent-name is required.
--agent-namestringNoRequesting agent name (resolved to ID server-side).
--actionstringYesThe proposed action.
--contact-emailstringYesContact email the action applies to.
--reasonstringYesReason for the request.
--timeoutnumberNoTimeout in minutes. Defaults to 5.
POST https://api.molted.email/v1/agent/coordination/consensus
curl
curl -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
  }'
FieldTypeRequiredDescription
tenantIdstringYesYour tenant identifier.
requestingAgentIdstringNoRequesting agent ID. Either requestingAgentId or proposerAgent is required.
proposerAgentstringNoRequesting agent name (resolved to ID server-side).
actionstringYesThe proposed action.
contactEmailstringYesContact email the action applies to.
reasonstringYesReason for the request.
timeoutMinutesnumberNoTimeout in minutes. Defaults to 5.

Response

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

CLI
molted agents consensus get CONSENSUS_ID
GET https://api.molted.email/v1/agent/coordination/consensus/:id
curl
curl "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

CLI
molted agents consensus vote CONSENSUS_ID \
  --agent-id agent_f6e5d4c3b2a1 --vote approve --reason "Agreed, signals look bad"

Options

FlagTypeRequiredDescription
--agent-idstringYesVoting agent ID.
--votestringYesYour vote: approve or reject.
--reasonstringNoReason for your vote.
POST https://api.molted.email/v1/agent/coordination/consensus/:id/vote
curl
curl -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"
  }'
FieldTypeRequiredDescription
agentIdstringYesVoting agent ID.
votestringYesYour vote: approve or reject.
reasonstringNoReason for your vote.

Vote response

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

StatusDescription
pendingVoting is still open.
approvedMajority of active agents voted approve.
rejectedMajority of active agents voted reject.
expiredThe vote timed out before reaching a decision.

Agent config

Each agent can store configuration that controls behavior like humanizer settings.

Get config

CLI
molted agents config AGENT_ID
GET https://api.molted.email/v1/agent/agents/:agentId/config
curl
curl "https://api.molted.email/v1/agent/agents/agent_a1b2c3d4e5f6/config?tenantId=tenant_abc123" \
  -H "Authorization: Bearer YOUR_API_KEY"

Update config

CLI
molted agents update-config AGENT_ID \
  --config '{"humanizer_enabled": true, "humanizer_style": "professional"}'
PUT https://api.molted.email/v1/agent/agents/:agentId/config
curl
curl -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

KeyTypeDescription
humanizer_enabledbooleanEnable AI email humanization for this agent.
humanizer_stylestringHumanizer writing style (e.g., professional, casual).
humanizer_providerstringHumanizer provider to use.

Unrecognized keys are silently stripped.