Verification Gateway SDK
Verify incoming AI agent requests and present agent credentials across HTTP, A2A, and MCP.
The Verification Gateway SDK enables any service to verify incoming AI agent requests and enables
agents to present their credentials across HTTP, A2A, and MCP protocols. The gateway sits in
front of your service, calls AstraSync's /api/agents/verify-access endpoint on every request,
and surfaces a structured policy decision your route handlers can act on. Current release and
full changelog:
@astrasyncai/verification-gateway on npm.
npm install @astrasyncai/verification-gatewayWhat you get
- Drop-in Express and Next.js middleware. Mount once, declare per-route policy, requests
come back annotated with
req.agentVerification. - Server-decided
accessLevel(none / guidance / read-only / standard / full / internal). The SDK reads it verbatim — no client-side trust-score remap (v2.3.0+ contract). - Anonymous-traffic handling per the
unverifiedAgentPolicyyou set on each registered endpoint —deny/audit/allow_partial/allow_full. Recognised platform agents (Claude / ChatGPT / Gemini / Cursor / Goose) get an auto-provisioned ASTRA-id with IAL=1.auditmode (v2.3.8+) lets requests through with anX-Astra-Unverified-Warningresponse header for soft-launch deployments. - Optional
setPassThroughHeader(v2.3.8+) emitsX-Astra-Gateway-Mode: pass-throughon responses where the middleware fell through without consulting verify-access. Lets integration tests assert "this endpoint should be gated; if it falls through, fail loudly." PlusdashboardUrlto customise the link in boot-time configuration warnings. - HMAC-SHA256 webhook signature verification via
verifyAstraSyncWebhook(rawBody, headers, secret)— Stripe-styleX-AstraSync-Signatureheader, 5-min replay tolerance, constant-time compare. - MCP and A2A protocol helpers, init-time self-test (HEAD probe to catch misconfigured
apiBaseUrl), optionalcounterpartyIdconfig for multi-endpoint attribution.
Two SDKs, two roles — pick by who you are
AstraSync ships two npm packages. They are not alternatives — they sit on opposite sides of the verify-access call.
You receive traffic from AI agents and want to enforce policy on it.
Install @astrasyncai/verification-gateway.
Express / Next.js middleware, MCP / A2A adapters, the verify() helper, and the webhook
signature verifier all live here.
You build the AI agent that authenticates against AstraSync and calls merchants.
Install @astrasyncai/verification-gateway.
Agent registration, credential presentation (prepareMcpMeta, prepareA2AMetadata),
trust-score helpers, and KYD onboarding flows.
One end-to-end verification involves both: the agent author's SDK signs the request; the merchant's gateway verifies it. If you're unsure which one to install, the rule is verification-gateway = the side that answers, agent-registration = the side that asks.
Counterparty-Side: Verifying Incoming Agents
Mount Express middleware to verify agents hitting your API. The middleware calls AstraSync's
verify-access endpoint and populates req.agentVerification with the result.
import { createMiddleware } from '@astrasyncai/verification-gateway/express';
// v2.3.7+ — per-route policy lives in the AstraSync dashboard.
// The SDK fetches it on init via counterpartyId; do NOT pass routes here.
app.use(
createMiddleware({
apiBaseUrl: 'https://astrasync.ai/api', // always includes /api
apiKey: process.env.ASTRASYNC_API_KEY,
counterpartyId: 'ASTRAE-...', // your endpoint id from the dashboard
setPassThroughHeader: true, // recommended for staging — surfaces
// pass-through mode when no policy
// is configured (see Endpoint Management)
})
);After middleware runs, req.agentVerification contains (v2.3.0+):
.verified— whether the agent passed verification.accessLevel— server-decided level (none, restricted, read-only, standard, full, internal). The SDK reads this verbatim from the verify-access response — no client-side trust-score remap (v2.3.0+ contract). v2.3.9 (defect #30): renamedguidanceband →restricted. See the trust-score tier cheatsheet below for the canonical mapping..agent—astraId,name,trustScore,agentStatus(wasstatus),blockchainStatus(new:verified|pending|failed|unverified).advisory— present on anonymous calls (v2.3.0+).{ial, registrationUrl, docsUrl, policy, restrictionsExplained}tells the agent how to upgrade based on the endpoint'sunverifiedAgentPolicy..pdlss— full permission boundaries.tokenGuidance— recommended scopes, TTL, rate limits.recommendation— grant, deny, or step_up_required
Trust-score tier cheatsheet
The server resolves accessLevel from the agent's live trust score against the endpoint's
trustScoreRequirement. Canonical thresholds (defined in
apps/backend/src/utils/access-levels.ts and shipped verbatim by the SDK):
| Trust score | accessLevel | What the SDK does |
|---|---|---|
0–19 | none / restricted | Anonymous policy applies; verified agents below threshold get blocked or restricted-only. |
20–39 | read-only | GET-style routes; no writes / payments. |
40–69 | standard | Default tier for verified agents — POSTs, transactions within PDLSS limits. |
70+ | full | Unrestricted within boundary; high-trust agents. |
| same-org | internal | Calling agent shares the endpoint's owner — bypasses PDLSS scope checks. |
Set routes[].minAccessLevel in the middleware config to gate routes by tier. The mapping is
server-authoritative — DO NOT pass minTrustScore / minTrustScoreForFull to GatewayConfig
(deprecated since v2.3.0).
Anonymous traffic is now first-class (v2.3.0+)
When an agent hits your endpoint without an ASTRA-id, the SDK forwards the call to the server with
no agentId. The server applies the endpoint's unverifiedAgentPolicy (see the Endpoint PDLSS
section):
deny→access.allowed=false, advisory points the caller at registration.audit(v2.3.8+) →access.allowed=true+X-Astra-Unverified-Warningresponse header, activity feed records "granted (audit)".allow_partial→access.accessLevel="restricted", advisory explains restrictions + how to upgrade. (v2.3.9: wasguidance, renamed to remove value-name collision with the help-payload object.)allow_full→access.accessLevel="standard", advisory recommends registration for next time.
Every branch always emits a verification event + queues a blockchain audit record. Recognised platform agents (Claude / ChatGPT / Gemini / Cursor / Goose) get an auto-provisioned provisional ASTRA-id (IAL=1) so they appear in the AstraSync admin panel under the auto-provisioned tab.
Configure attribution + init self-test (v2.3.0+)
createMiddleware({
apiBaseUrl: 'https://astrasync.ai/api', // always include /api
counterpartyId: 'ASTRAE-xxxxx', // your endpoint's ASTRAE-id from registration
counterpartyUrl: 'https://merchant.example',
// disableInitChecks: true // skip the HEAD probe in tests
});counterpartyId tells the server to attribute traffic by ASTRAE-id rather than resolving by URL —
useful when the same merchant runs multiple endpoints under one origin (e.g. /checkout + /mcp
with separate policies).
On first verify() call the SDK fires a HEAD probe to {apiBaseUrl}/agents/verify-access and
warns once if the response content-type is text/html — catches the case where apiBaseUrl is
pointing at a marketing 404.
Enhanced Verification with Sessions
import { verify } from '@astrasyncai/verification-gateway';
const result = await verify(config, {
credentials: { astraId: 'ASTRA-xxxxx' },
purpose: 'financial_transaction',
createSession: true,
enableRuntimeChallenge: true,
});
// result.sessionId, result.runtimeChallenge, result.tokenGuidanceRecording a Decision
import { recordDecision } from '@astrasyncai/verification-gateway';
await recordDecision(config, {
sessionId: result.sessionId,
decision: 'granted',
reason: 'Agent meets all requirements',
tokenIssued: true,
});Agent-Side: Presenting Credentials
Use AgentClient to automatically inject AstraSync credentials into outgoing requests across all
supported protocols.
import { AgentClient } from '@astrasyncai/verification-gateway';
const client = new AgentClient({
agentId: 'ASTRA-xxxxx',
challengeUrl: 'https://my-agent.com/astrasync/challenge',
pdlss: {
purpose: { category: 'read_data' },
duration: { maxSessionDuration: 3600 },
scope: { jurisdiction: 'US' },
},
});
// HTTP — headers auto-injected
const response = await client.fetch('https://counterparty.com/api/data');
// A2A — metadata.astrasync block auto-added
const task = client.prepareA2AMetadata({ id: 'task-1' });
// MCP — _meta.astrasync block auto-added
const params = client.prepareMcpMeta({ tool: 'search', query: '...' });X-Astra-* Headers (HTTP Transport)
| Header | Value | Example |
|---|---|---|
X-Astra-ID | Agent ASTRA ID | ASTRA-wEDhecjrlWEqPNQU4htX6g |
X-Astra-Verify | Verify-access URL | https://astrasync.ai/api/agents/verify-access |
X-Astra-Challenge | Challenge endpoint | https://my-agent.com/astrasync/challenge |
X-Astra-Purpose | Purpose category:action | read_data:search |
X-Astra-Duration | Max session seconds | 3600 |
X-Astra-Scope | Jurisdiction | US |
Challenge/Response Mechanism
Runtime challenges verify that the agent actually initiated the request (prevents MITM attacks). The flow works as follows:
Verification Gateway Endpoint is optional. Agents register an endpoint URL only if they want
to be challenged at runtime. Agents registered without an endpoint still verify successfully,
but the verify-access response surfaces runtimeChallengeSupported: false so counterparties can
factor that into their decision. This is a trust signal, not a hard requirement.
- Agent registers pending counterparties before initiating contact
- Agent makes request to counterparty with AstraSync headers
- Counterparty calls verify-access with
enableRuntimeChallenge: true - AstraSync POSTs challenge to agent's
/astrasync/challengeendpoint - Agent's ChallengeHandler responds with pending counterparty list
- AstraSync validates (challengeId match, counterparty in list, timestamp fresh)
Setting Up the ChallengeHandler
import { ChallengeHandler } from '@astrasyncai/verification-gateway';
const handler = new ChallengeHandler({ agentId: 'ASTRA-xxxxx' });
// Before contacting a counterparty, register them as pending
handler.registerPending('counterparty-api-id');
// Mount the challenge endpoint
app.post('/astrasync/challenge', handler.expressMiddleware());
// After interaction is complete, clean up
handler.removePending('counterparty-api-id');Challenge Payload (sent to agent)
{
"challengeId": "uuid",
"type": "pending_verification",
"counterpartyId": "counterparty-api-id",
"question": "List the counterparties currently in your pending interaction list",
"issuedAt": "2026-02-24T10:00:00Z",
"expiresAt": "2026-02-24T10:00:30Z"
}Expected Response (from agent)
{
"challengeId": "uuid",
"acknowledged": true,
"pendingCounterparties": ["counterparty-api-id", "other-service"],
"respondedAt": "2026-02-24T10:00:01Z"
}
