docs / agent-messaging-protocol / api reference

API reference

Most of the surface below is exported from fg_amp (__all__). Exceptions — import from their submodules: the Signing functions (fg_amp.signing), the Capabilities tokens and helpers (fg_amp.capabilities), RestoredState (fg_amp.session), and BUILTIN_BODY_TYPES / is_typed_name (fg_amp.bodies). HttpTransport and create_inbox_router lazy-load and require the [http] extra. noted. HttpTransport and create_inbox_router are lazy-loaded and require the [http] extra.

AmpNode

The composition root: owns the identity, runs handshakes, enforces policy, manages sessions and groups.

AmpNode(
    identity,                                # AgentIdentity (or with_delegation(chain))
    policy=None,                             # ContactPolicy — None accepts nothing inbound
    payload_types=DEFAULT_PAYLOAD_TYPES,
    endpoints=None,                          # e.g. {"wake": "https://host/wake"}
    approval_fn=None,                        # async human-approval hook for policy
    on_session=None,                         # async callback(Session) on inbound accept
    on_group=None,                           # async callback(GroupSession)
    session_store=None,                      # SessionStore for persistent sessions
    capabilities=None,                       # capability token offer
    witness=None,                            # WitnessSpec for witnessed posture
)
Member Description
.address -> str Self-certifying amp:key:... address
.owner -> str \| None Root of the node's own delegation chain
.card -> AgentCard Signed card (re-register after rotate_prekey)
attach(transport) / detach() Bind/unbind the node's address on a transport
async aclose(reason="node shutting down") Close all sessions and shut down
async initiate(peer_card, purpose="", mode=SessionMode.EPHEMERAL, payload_types=None, ttl_seconds=3600.0, timeout=30.0, wait=True) Handshake with a peer. Returns Session, or PendingInitiation when wait=False
async create_group(member_cards, purpose="", mode=SessionMode.EPHEMERAL, ttl_seconds=3600.0) Full-mesh GroupSession
persist_session(session) -> SessionRecord Export a persistent session (no key material)
async resume(session_id, timeout=30.0) -> Session Resume from the session store
rotate_prekey() -> AgentCard Rotate the agreement prekey ring; returns the new card
metrics() -> dict[str, int] Aggregate session counters + live_sessions
forget_expired_initiations() -> int Reap expired deferred knocks
forget_closed_sessions() -> int Reap closed sessions
.on_witness_copy Handler for outbound witness copies (e.g. WitnessReceiver.handle)
.revocations The node's RevocationRegistry
.sessions, .groups Live session / group maps

PendingInitiation (from initiate(wait=False)): .session_id, .done -> bool, async wait(timeout=None) -> Session, cancel().

Session

Never constructed directly — built by the node after the handshake.

Method Description
async send(payload: Payload) -> Envelope Encrypt and send
async send_text(text, **metadata) text/plain convenience
async send_json(data, **metadata) JSON convenience
async send_body(model, **metadata) Typed body model (validated before encrypt)
async receive(timeout=None) -> ReceivedMessage Next in-order message
receive_nowait() -> ReceivedMessage \| None Non-blocking variant
async close(reason="") Authenticated close; burns key material
has_scope(scope) -> bool Peer's handshake-verified scopes
require_scope(scope, *, owner=None) Raises SessionError; owner also asserts peer_owner
transcript_matches(expected_head: bytes) -> bool Compare transcript heads
max_inflight Backpressure cap (None or 1..256)
arm_maintenance() / cancel_maintenance() / set_retransmit_interval(seconds \| None) Reliability-timer control

Key attributes: session_id, mode, state, peer_card, peer_owner, peer_scopes, purpose, capabilities (frozenset), payload_types, created_at, expires_at, initiator, transcript, stats (SessionStats), tasks (TaskTracker), payments (PaymentTracker), spend / peer_spend (SpendLedger), witness, callbacks on_payload / on_closed.

Supporting types:

Session store

Groups

Witness

Policy

ContactPolicy (frozen pydantic). Fields: mode, require_scopes, trusted_issuers, allow_addresses, allow_operators, allow_kinds, accepted_payload_types, accept_modes, max_sessions=256, rate_limit_per_peer=10, rate_limit_window_seconds=60.0, max_ttl_seconds=86400.0, human_approval=False.

Constructors: ContactPolicy.open(), ContactPolicy.credentialed(scopes, trusted_issuers=None), ContactPolicy.allowlist(addresses=None, operators=None).

PolicyMode: OPEN, CREDENTIALED, ALLOWLIST, CLOSED. (PolicyEngine, Decision, PolicyResult, ApprovalFn are internal but importable from fg_amp.policy.)

Envelope

Envelope (frozen, extra="allow"): amp="0.1", id, type: EnvelopeType, sender (wire "from"), to, session_id, seq=0, created_at, body (b64), sig. Methods: .signed(keys) -> Envelope, .verify_signature(), .body_bytes, Envelope.encode_body(raw), .to_wire(), Envelope.from_wire(data).

EnvelopeType: handshake.initiate, handshake.accept, handshake.reject, session.message, session.close, session.resume, resume.accept, resume.reject, receipt, witness.copy.

Typed bodies

Registry: BodyRegistry, BodyType, default_registry, is_typed_name, BUILTIN_BODY_TYPES. All built-in bodies are frozen=True, extra="allow".

Type Classes Key fields / enums
amp.task/1 TaskBody, TaskTracker task_id, kind, title, body, inputs, outputs, deadline, refs; TaskKind: request, accept, reject, progress, complete, fail, cancel; TaskState: requested, accepted
amp.payment/1 PaymentBody, PaymentTracker, SpendLedger payment_id, kind, amount (decimal string), asset, pay_to, valid_until, x402, chain_ref, tx_ref, reason; PaymentKind: quote, authorization, settled, failed; PaymentState: quoted, authorized
amp.mcp/1 McpBody, McpBridge, McpHandler payload (one JSON-RPC 2.0 message, verbatim), mcp_session
amp.receipt/1 ReceiptBody ReceiptStatus: accepted, completed, rejected; exactly one of task_id / message_id
amp.ref/1 RefBody uri, kind, version, content_hash (sha256:<hex>); RefKind: artifact, proposal, ticket, commit, claim
amp.claim/1 ClaimBody, ClaimPedigree claim_id, statement, confidence (0..1), pedigree {source, evidence: tuple[RefBody], observed_at}, supersedes

McpBridge(session, handler=None, mcp_session=""): expose(handler), async call(request, timeout=30.0) (correlates the response by JSON-RPC id, assigns an amp-N id if absent), async dispatch(body) -> bool (returns False for other mcp_sessions so bridges layer), async pump(). McpHandler = Callable[[dict], Awaitable[dict]].

Identity (re-exported from fg-agent-id)

AgentIdentity, OwnerIdentity, ParticipantIdentity, AgentCard, ParticipantCard, ParticipantKind, Delegation, DelegationChain, KeyRevocation, Revocation, RevocationRegistry, address_to_did, did_to_address, did_document, resolve, signing_key_from_did.

Common calls: AgentIdentity.generate(name), OwnerIdentity.generate(name), owner.create_agent(name, scopes), identity.with_delegation(chain), Delegation.grant(keys, issuer_addr, subject_addr, scopes, ttl_seconds).

Transports

Abstract base: Transport.deliver(envelope), .bind(address, handler), .unbind(address); InboundHandler = Callable[[Envelope], Awaitable[None]]. Transports move opaque signed envelopes and never inspect bodies.

Class Notes
InMemoryTransport Same-process mesh; store-and-forward for unbound addresses (cap 1024/address); receiver faults logged, never propagated to the sender
HttpTransport (http extra, lazy) Direct agent-to-agent POST to a peer inbox; SSRF-guarded; create_inbox_router builds the FastAPI inbox + /.well-known card route
RelayTransport HTTP client to one or more relays: card registration, mailbox long-poll, discovery, revocation sync; failover across ordered URLs (first definitive answer wins, 4xx never fails over); .for_card(card) builds the target set from a peer's card
WsRelayTransport Drop-in RelayTransport subclass; real-time push over /amp/v0/relay/ws, falls back to authenticated HTTP pulls when the socket is down
RelayState / SqliteRelayState + create_relay_app The hosted relay: encrypted mailboxes, signed-card directory, revocation lists, rate limiting, optional wake and federation
RelaySyncer Federation: pulls a peer relay's cards/revocations/key-revocations deltas, verify-before-admit; mailboxes deliberately do not federate

Console script: amp-relay --port 8404 (--peer URL --sync-interval N for federation).

Wake

WakeNotifier (debounced, host-keyed, drop-on-saturation ping sender; schedule(address, card) fires in the background and never raises or blocks), WakePolicy (SSRF policy: https-only, no private/loopback/metadata, connect-time rebinding guard, no redirects), WakeReceiver (listener; [http] extra; coalesce=True by default), WakeError.

Signing

DOMAIN = "fg-amp/v1"; contexts envelope, group-roster, relay-pull, relay-ack, relay-ws. Functions: domain_tag(context), signing_input(context, payload), sign_payload(keys, context, payload), decode_signature(sig) (rejects non-canonical base64), verify_payload(public, context, payload, sig), verify_by_address(address, context, payload, sig).

Capabilities

CAP_RATCHET_DH_V1 = "amp.ratchet.dh-v1" (baseline), CAP_PQ_ML_KEM_768 = "amp.kem.ml-kem-768-x25519", CAP_WITNESSED_V1 = "amp.posture.witnessed-v1"; default_capabilities(), negotiate(local, remote) -> sorted intersection.

Errors

Base AmpError, with subclasses: DecryptionError, PolicyRejection(reason), SessionError, SessionStateError, SessionNotFoundError, ResumeError, ConfigurationError, BodyError, BodyValidationError, UnknownBodyTypeError, TaskLifecycleError, PaymentLifecycleError, SpendRejectedError, WitnessError, ProtocolVersionError, SequenceError, TransportError.

Re-exported from fg-agent-id (subclass AgentIdError, not AmpError): AddressError, SignatureError, DelegationError.