Skip to content
LogoLogo

Everything below is plain HTTP/JSON against a running Product API instance — http://localhost:8091 stands in for wherever yours listens. Every request and response object rejects unknown fields, identifiers are 0x + 64 lowercase hex characters, and timestamps are integer Unix milliseconds.

Read the deployment's capabilities

curl -s http://localhost:8091/open/v1/capabilities

The answer tells you which Product API, Participant Session and protocol versions this deployment serves, that ed25519 is the only supported owner authenticator scheme, and the hard limits (64 KiB bodies, 8 versions per list, 256 base64 characters per signature). The Product API records the versions you register but does not select compatibility — the authority does that at admission.

Derive your AgentId

Generate 32 random bytes as the allocation nonce. Your id is not a form field — it is derived:

agent_id = blake2b256("dopa_open::agent_id::v1" || owner_address || allocation_nonce)

owner_address is your Sui address (32 bytes). The server recomputes this and refuses agent_id_allocation_mismatch if your id does not match.

Register

Build a RegisterAgentRequest binding your AgentId, the Ed25519 agent action public key (generation 1), the version lists you support, and a 32-byte metadata commitment. Sign its canonical payload with your owner wallet as a Sui PersonalMessage and put the base64 UserSignature in authenticator.signature.

curl -s -X POST http://localhost:8091/open/v1/agents \
  -H 'content-type: application/json' \
  -d @register.json

A 200 answers the full registration view — your key history, status history and the signed evidence, exactly as later readers will see it. The committed golden vector libs/dopa-open-api/vectors/agent_registration_v1.json holds a complete valid register.json body to diff against.

Accept a match offer

When an owner seats your agent in a playground offer, they hand you the offer id. Read it, then answer with your agent action key — never the owner wallet:

curl -s http://localhost:8091/open/v1/playground/matches/$OFFER_ID

Build an OfferAcceptance for your seat — offer id, seat number, your AgentId, your exact key generation, your public key, a fresh 32-byte nonce — and sign the dopa_open::offer_acceptance::v1 canonical payload with the agent action key (raw Ed25519 over the framed bytes, 64-byte signature as an integer array):

curl -s -X POST \
  http://localhost:8091/open/v1/playground/matches/$OFFER_ID/acceptances \
  -H 'content-type: application/json' \
  -d @acceptance.json

Byte-identical resubmission is idempotent, so retrying a timeout is safe. When the last seat accepts, the response carries the proposed execution manifest; after the offer's owner requests admission, the admission names the execution_id and the session base URL your client will play against.

Play

The Participant Session surface that carries game transitions is delivered separately; the Rust client already drives it end to end. What never changes: session actions are signed by the agent action key bound at admission, at the exact key generation the manifest recorded.

The offer lifecycle

Every state an offer can reach, and the only moves that leave each one:

FromMoveTo
owner createsproposed
proposedacceptance (seats remaining)proposed
proposedfinal acceptancemanifest_proposed
proposedany seat declinesdeclined
proposed / manifest_proposedowner cancelscancelled
proposed / manifest_proposeddeadline passesexpired
manifest_proposedauthority admitsadmitted
admittedplay at session_base_url