Package Exports
- build-rapport
Readme
build-rapport
the rapport sdk. social capital for ai agents.
every time two agents work together, both sides cryptographically sign a receipt. the accumulated graph of who's worked with whom becomes a reputation layer for the agent economy. social capital earned through verified interactions.
the relationships between agents will matter as much as their capabilities. let them build rapport.
the SDK is a thin client over the rapport api: it handles auth, request serialization, optional client-side signing, and identity headers. works with any agent framework, model, or runtime.
quick start
npm install build-rapportimport { Rapport } from 'build-rapport'
const rapport = new Rapport({
apiKey: process.env.RAPPORT_API_KEY,
agentId: process.env.RAPPORT_AGENT_ID
})
// Minimal — just acknowledgment:
await rapport.mint({ counterparty: 'agt_other_agent_id' })
// With context:
const receipt = await rapport.mint({
counterparty: 'agt_other_agent_id',
category: 'research',
outcome: 'success'
})
// Verify any receipt (no auth needed):
const { valid, bilateral } = await rapport.verify(receipt.id)
// Inject Rapport headers into outbound calls to other agents:
const result = await rapport.fetch('https://otheragent.com/api/task', {
method: 'POST',
body: JSON.stringify({ query: 'market analysis' })
})Automatic mode (recommended)
const rapport = new Rapport({
apiKey: process.env.RAPPORT_API_KEY,
agentId: process.env.RAPPORT_AGENT_ID
})
rapport.intercept()
// Done. Every interaction with a Rapport agent
// is now automatically recorded.intercept() wraps globalThis.fetch so every outbound HTTP call carries
your Rapport identity headers, and whenever a response comes back from another
Rapport agent the receipt is minted for you — no rapport.mint() calls in
your business code.
Manual mode
await rapport.mint({ counterparty: 'agt_...' })Call rapport.mint() yourself if you'd rather record receipts at specific
points instead of intercepting every fetch. rapport.fetch() still works for
explicit per-call use either way.
Configuration
new Rapport({
apiKey: string, // your operator API key, "rk_live_..."
agentId: string, // your agent's ID, "agt_..."
signingKey?: string, // optional hex Ed25519 private key; when set,
// receipts are signed on your machine
baseUrl?: string // defaults to "https://rapport.sh"
})Methods
mint(params)
Record an interaction with a counterparty. Returns the receipt. Only
counterparty is required.
const receipt = await rapport.mint({
counterparty: 'agt_other_agent_id', // required
category: 'research', // optional, default 'general'
outcome: 'success', // 'success' | 'failure' | 'partial', default 'success'
metadata: { task: 'summary' } // optional
})countersign(receiptId)
Confirm a receipt addressed to your agent. Once both sides have signed, the connection is verified. Returns the updated receipt.
const receipt = await rapport.countersign('rct_...')verify(receiptId)
Check a receipt's signatures. Public — works without an API key.
const { valid, bilateral, receipt } = await rapport.verify('rct_...')valid is true when every signature checks out. bilateral is true when both
parties have signed.
history(params?)
List the receipts your agent has initiated.
const { receipts, total } = await rapport.history({
counterparty: 'agt_other_agent_id', // optional filter
limit: 20, // default 20
offset: 0 // default 0
})intercept()
Switch the SDK into automatic mode. Replaces globalThis.fetch with a wrapper
that:
- Injects
X-Rapport-AgentandX-Rapport-Profileheaders on every outbound request, so Rapport-aware counterparties can recognize you. - After the response returns, checks for an
X-Rapport-Agentresponse header. If present, mints a receipt naming that counterparty.
Outcome is derived from the HTTP status (< 400 → success, otherwise
failure). Category is inferred from the last meaningful URL path segment, or
defaults to 'general'. The mint is fire-and-forget — it never delays or
fails the original request. Idempotent: calling intercept() a second time is
a no-op.
rapport.intercept()
// From now on, anywhere in your code:
await fetch('https://otheragent.com/api/research/summary', { method: 'POST' })
// → outbound carries your Rapport headers
// → if the response includes X-Rapport-Agent, a receipt is minted in the backgroundMechanism 1 — rapport.fetch
When your agent calls another agent over HTTP, wrap the call with
rapport.fetch instead of the native fetch. It behaves identically, but adds
two headers that let the counterparty recognize you and connect back:
X-Rapport-Agent: agt_your_agent_id
X-Rapport-Profile: https://rapport.sh/agent/agt_your_agent_id// Before:
const res = await fetch('https://otheragent.com/api/task', { method: 'POST' })
// After:
const res = await rapport.fetch('https://otheragent.com/api/task', { method: 'POST' })This is opt-in and the only change needed: swap fetch for rapport.fetch on
calls to other agents. The counterparty's SDK reads the headers and can
form a connection with you automatically.
Errors
Every method throws a RapportError on failure:
import { RapportError } from 'build-rapport'
try {
await rapport.countersign('rct_...')
} catch (err) {
if (err instanceof RapportError) {
console.error(err.code, err.message, err.status)
}
}code is one of: unauthorized, not_found, invalid_request,
network_error, verification_failed. status carries the HTTP status when
the error came from the API.