Integration Pack for developers and AI coding assistants.
Use this page to wrap existing Python, JavaScript, and TypeScript agents with the published a2spa package and public A2SPA APIs.
POST /api/verify_payload, or treats local helper checks as enforcement.1. Create Agents
Use the authenticated dashboard to create agents and save issued private keys immediately.
2. Wrap Runtime Code
Install and use the published a2spa package to build canonical payloads, sign locally, call A2SPA, and poll inbox endpoints.
3. Stay API-Only
A2SPA remains the verification authority for signatures, policy, replay protection, status, and credits.
Step 1
Create or rotate the needed agents in the authenticated dashboard first.
Step 2
Save the returned private key immediately; it is only shown once.
Step 3
Add the A2SPA environment variables to the target project.
Step 4
Install the published a2spa package and adapt the minimal language example for the target project.
Step 5
Use the inbox_for_agent endpoint to poll received messages for the receiving agent.
Required
Always send signed payloads through POST /api/verify_payload.
Required
Put policy-relevant semantics in signed input fields such as intent, action, workflow_scope, amount_usd, currency, and on_behalf_of.
Required
When using A2SPA 2.0 state_continuity, register the authoritative state through POST /api/continuity_state first.
Required
Always fetch received runtime messages through GET /api/inbox_for_agent.
Required
Use GET /api/logs_for_agent for audit and troubleshooting, not as the main runtime inbox.
Required
Use the published a2spa package for payload construction and signing where available.
Required
Do not verify payloads locally as a substitute for the API.
Required
Do not read or write A2SPA backend storage directly from customer agent code.
Required
Do not bypass nonce, timestamp, signature, permission, or toggle checks.
Required
Store the agent private key outside source control and outside public directories.
Verify Payload
https://api.aimodularity.com/A2SPA/api/verify_payloadSubmit signed payloads for runtime authorization.
Agent Inbox
https://api.aimodularity.com/A2SPA/api/inbox_for_agentPoll received messages for a target agent through A2SPA.
Agent Logs
https://api.aimodularity.com/A2SPA/api/logs_for_agentUse logs for audit and troubleshooting, not as the primary inbox.
curl -X POST https://api.aimodularity.com/A2SPA/api/verify_payload \
-H "Content-Type: application/json" \
-H "x-api-key: $A2SPA_API_KEY" \
-d @signed-request.json
Use the published a2spa package.
Environment
.env.a2spa.example
Python Package Example
a2spa_client.py
JavaScript Package Example
a2spa-client.mjs
TypeScript Package Example
a2spa-client.ts
A2SPA_API_BASE=https://api.aimodularity.com/A2SPA
A2SPA_API_KEY=YOUR_API_KEY_HERE
A2SPA_AGENT_ID=your_user_id_my_agent_send
A2SPA_PRIVATE_KEY_PATH=your_sender_agent.priv.pem
A2SPA_TARGET_AGENT_ID=your_user_id_my_agent_receive
import json
import os
from pathlib import Path
import requests
from a2spa import (
build_payload_fields as package_build_payload_fields,
build_request_body,
build_signed_request as build_a2spa_signed_request,
compute_payload_hash,
finalize_payload,
policy_input,
require_authorized_result,
)
# Install with: pip install a2spa requests
class A2SPAClient:
def __init__(self, api_base, api_key, agent_id, private_key_path, timeout=20):
self.api_base = api_base.rstrip("/")
self.api_key = api_key
self.agent_id = agent_id
self.private_key_path = private_key_path
self.timeout = timeout
self._private_key = None
@classmethod
def from_env(cls):
return cls(
api_base=os.environ.get("A2SPA_API_BASE", "https://api.aimodularity.com/A2SPA"),
api_key=os.environ["A2SPA_API_KEY"],
agent_id=os.environ["A2SPA_AGENT_ID"],
private_key_path=os.environ["A2SPA_PRIVATE_KEY_PATH"],
)
def _load_private_key(self):
if self._private_key is None:
self._private_key = Path(self.private_key_path).read_text(encoding="utf-8")
return self._private_key
def build_payload_fields(self, target_agent_id, input_data, output_data=None, alert_threshold=10, state_continuity=None):
return package_build_payload_fields(
agent_id=self.agent_id,
target_agent_id=target_agent_id,
input_data=input_data,
output_data=output_data if output_data is not None else {"status": "ready"},
alert_threshold=alert_threshold,
state_continuity=state_continuity,
)
def build_signed_request(self, target_agent_id, input_data, output_data=None, alert_threshold=10, state_continuity=None):
payload = self.build_payload_fields(target_agent_id, input_data, output_data, alert_threshold, state_continuity)
return build_a2spa_signed_request(self._load_private_key(), payload)
def post_request_body(self, request_body):
return requests.post(
f"{self.api_base}/api/verify_payload",
headers={
"Content-Type": "application/json",
"x-api-key": self.api_key,
},
json=request_body,
timeout=self.timeout,
)
def send_payload(self, target_agent_id, input_data, output_data=None, alert_threshold=10, state_continuity=None, debug=False):
request_body = self.build_signed_request(target_agent_id, input_data, output_data, alert_threshold, state_continuity)
response = self.post_request_body(request_body)
if debug and response.status_code >= 400:
print(json.dumps({"status_code": response.status_code, "error": response.text}, indent=2))
response.raise_for_status()
result = response.json()
return require_authorized_result(result)
def fetch_logs(self, agent_id=None):
response = requests.get(
f"{self.api_base}/api/logs_for_agent",
headers={"x-api-key": self.api_key},
params={"agent_id": agent_id or self.agent_id},
timeout=self.timeout,
)
response.raise_for_status()
return response.json().get("logs", [])
def fetch_inbox(self, agent_id=None, after=None, limit=50):
params = {"agent_id": agent_id or self.agent_id, "limit": limit}
if after:
params["after"] = after
response = requests.get(
f"{self.api_base}/api/inbox_for_agent",
headers={"x-api-key": self.api_key},
params=params,
timeout=self.timeout,
)
response.raise_for_status()
return response.json().get("messages", [])
def prepare_and_send(self, target_agent_id, input_data, runner, alert_threshold=10):
# The runner must only prepare output_data. Do not perform side effects before A2SPA authorization.
output_data = runner(input_data)
return self.send_payload(target_agent_id, input_data, output_data, alert_threshold)
def existing_agent(input_data):
return {
"status": "ready",
"summary": f"Processed: {input_data.get('message', 'no message')}",
}
if __name__ == "__main__":
client = A2SPAClient.from_env()
target_agent_id = os.environ["A2SPA_TARGET_AGENT_ID"]
input_data = policy_input(action="send_message", workflow_scope="messages:send", message="Hello from my existing Python agent")
output_data = existing_agent(input_data)
signed_request = client.build_signed_request(target_agent_id, input_data, output_data)
response = client.post_request_body(signed_request)
print(f"A2SPA verify response: {response.status_code} {response.text}")
import fs from "node:fs/promises";
import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
const require = createRequire(import.meta.url);
const a2spa = require("a2spa");
const { buildPayloadFields, buildSignedRequest, policyInput, requireAuthorizedResult } = a2spa;
export const canonicalizeA2spaPayload = a2spa.canonicalizeA2spaPayload;
export const computePayloadHash = a2spa.computePayloadHash;
export const prepareSignedBytes = a2spa.prepareSignedBytes;
export const finalizePayload = a2spa.finalizePayload;
export const buildRequestBody = a2spa.buildRequestBody;
export const signPayload = a2spa.signPayload;
export { buildPayloadFields, buildSignedRequest, policyInput, requireAuthorizedResult };
// Install with: npm install a2spa
function requireEnv(name) {
const value = process.env[name];
if (!value) {
throw new Error(`Missing required environment variable: ${name}`);
}
return value;
}
export class A2SPAClient {
constructor({ apiBase, apiKey, agentId, privateKeyPath, timeoutMs = 20000 }) {
this.apiBase = apiBase.replace(/\/$/, "");
this.apiKey = apiKey;
this.agentId = agentId;
this.privateKeyPath = privateKeyPath;
this.timeoutMs = timeoutMs;
this.privateKeyPem = null;
}
static fromEnv() {
return new A2SPAClient({
apiBase: process.env.A2SPA_API_BASE || "https://api.aimodularity.com/A2SPA",
apiKey: requireEnv("A2SPA_API_KEY"),
agentId: requireEnv("A2SPA_AGENT_ID"),
privateKeyPath: requireEnv("A2SPA_PRIVATE_KEY_PATH"),
});
}
async loadPrivateKeyPem() {
if (!this.privateKeyPem) {
this.privateKeyPem = await fs.readFile(this.privateKeyPath, "utf8");
}
return this.privateKeyPem;
}
async buildSignedRequest(targetAgentId, input, output = { status: "ready" }, options = {}) {
const payload = buildPayloadFields({
agentId: this.agentId,
targetAgentId,
input,
output,
alertThreshold: options.alertThreshold ?? 10,
stateContinuity: options.stateContinuity ?? null,
});
return buildSignedRequest(payload, await this.loadPrivateKeyPem());
}
async postRequestBody(requestBody) {
return fetch(`${this.apiBase}/api/verify_payload`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": this.apiKey,
},
body: JSON.stringify(requestBody),
});
}
async sendPayload(targetAgentId, input, output = { status: "ready" }, options = {}, debug = false) {
const requestBody = await this.buildSignedRequest(targetAgentId, input, output, options);
const response = await this.postRequestBody(requestBody);
const result = await response.json().catch(() => ({}));
if (!response.ok) {
if (debug) {
console.error(JSON.stringify({ status: response.status, error: result.error, reason_code: result.reason_code }, null, 2));
}
throw new Error(result.reason_code || result.error || `A2SPA did not authorize delivery (${response.status})`);
}
return requireAuthorizedResult(result);
}
async fetchLogs(agentId = this.agentId) {
const params = new URLSearchParams({ agent_id: agentId });
const response = await fetch(`${this.apiBase}/api/logs_for_agent?${params.toString()}`, {
headers: { "x-api-key": this.apiKey },
});
if (!response.ok) {
throw new Error(await response.text());
}
const body = await response.json();
return body.logs || [];
}
async fetchInbox(agentId = this.agentId, after = "", limit = 50) {
const params = new URLSearchParams({ agent_id: agentId, limit: String(limit) });
if (after) params.set("after", after);
const response = await fetch(`${this.apiBase}/api/inbox_for_agent?${params.toString()}`, {
headers: { "x-api-key": this.apiKey },
});
if (!response.ok) {
throw new Error(await response.text());
}
const body = await response.json();
return body.messages || [];
}
async prepareAndSend(targetAgentId, input, runner, options = {}) {
// The runner must only prepare output. Do not perform side effects before A2SPA authorization.
const output = await runner(input);
return this.sendPayload(targetAgentId, input, output, options);
}
}
async function existingAgent(input) {
return {
status: "ready",
summary: `Processed: ${input.message ?? "no message"}`,
};
}
async function main() {
const client = A2SPAClient.fromEnv();
const input = policyInput({ action: "send_message", workflowScope: "messages:send", message: "Hello from my existing Node agent" });
const output = await existingAgent(input);
const requestBody = await client.buildSignedRequest(process.env.A2SPA_TARGET_AGENT_ID, input, output);
const response = await client.postRequestBody(requestBody);
console.log(`A2SPA verify response: ${response.status} ${await response.text()}`);
}
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
main().catch((error) => {
console.error(error);
process.exit(1);
});
}
import fs from "node:fs/promises";
import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
import type { A2spaPayloadContent, JsonValue } from "a2spa";
const require = createRequire(import.meta.url);
const a2spa = require("a2spa") as typeof import("a2spa");
export const buildPayloadFields = a2spa.buildPayloadFields;
export const buildSignedRequest = a2spa.buildSignedRequest;
export const policyInput = a2spa.policyInput;
export const canonicalizeA2spaPayload = a2spa.canonicalizeA2spaPayload;
export const computePayloadHash = a2spa.computePayloadHash;
export const prepareSignedBytes = a2spa.prepareSignedBytes;
export const finalizePayload = a2spa.finalizePayload;
export const buildRequestBody = a2spa.buildRequestBody;
export const signPayload = a2spa.signPayload;
interface SendResult {
success?: boolean;
decision?: string;
reason_code?: string;
delivery?: { status?: string };
error?: string;
receipt?: unknown;
[key: string]: unknown;
}
interface LogEntry {
action?: string;
[key: string]: unknown;
}
// Install with: npm install a2spa
function requireEnv(name: string): string {
const value = process.env[name];
if (!value) {
throw new Error(`Missing required environment variable: ${name}`);
}
return value;
}
export class A2SPAClient {
private apiBase: string;
private apiKey: string;
private agentId: string;
private privateKeyPath: string;
private privateKeyPem?: string;
constructor(options: { apiBase: string; apiKey: string; agentId: string; privateKeyPath: string }) {
this.apiBase = options.apiBase.replace(/\/$/, "");
this.apiKey = options.apiKey;
this.agentId = options.agentId;
this.privateKeyPath = options.privateKeyPath;
}
static fromEnv(): A2SPAClient {
return new A2SPAClient({
apiBase: process.env.A2SPA_API_BASE || "https://api.aimodularity.com/A2SPA",
apiKey: requireEnv("A2SPA_API_KEY"),
agentId: requireEnv("A2SPA_AGENT_ID"),
privateKeyPath: requireEnv("A2SPA_PRIVATE_KEY_PATH"),
});
}
private async loadPrivateKeyPem(): Promise<string> {
if (!this.privateKeyPem) {
this.privateKeyPem = await fs.readFile(this.privateKeyPath, "utf8");
}
return this.privateKeyPem;
}
async buildSignedRequest(
targetAgentId: string,
input: A2spaPayloadContent,
output: A2spaPayloadContent = { status: "ready" },
options: { alertThreshold?: number; stateContinuity?: JsonValue | null } = {},
): Promise<ReturnType<typeof buildSignedRequest>> {
const payload = buildPayloadFields({
agentId: this.agentId,
targetAgentId,
input,
output,
alertThreshold: options.alertThreshold ?? 10,
stateContinuity: options.stateContinuity ?? null,
});
return buildSignedRequest(payload, await this.loadPrivateKeyPem());
}
async postRequestBody(requestBody: ReturnType<typeof buildSignedRequest>): Promise<Response> {
return fetch(`${this.apiBase}/api/verify_payload`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": this.apiKey,
},
body: JSON.stringify(requestBody),
});
}
async sendPayload(
targetAgentId: string,
input: A2spaPayloadContent,
output: A2spaPayloadContent = { status: "ready" },
options: { alertThreshold?: number; stateContinuity?: JsonValue | null } = {},
debug = false,
): Promise<SendResult> {
const requestBody = await this.buildSignedRequest(targetAgentId, input, output, options);
const response = await this.postRequestBody(requestBody);
const body = (await response.json().catch(() => ({}))) as SendResult;
if (!response.ok) {
if (debug) {
console.error(JSON.stringify({ status: response.status, error: body.error, reason_code: body.reason_code }, null, 2));
}
throw new Error(body.reason_code || body.error || `A2SPA did not authorize delivery (${response.status})`);
}
return requireAuthorizedResult(body) as SendResult;
}
async fetchLogs(agentId = this.agentId): Promise<LogEntry[]> {
const params = new URLSearchParams({ agent_id: agentId });
const response = await fetch(`${this.apiBase}/api/logs_for_agent?${params.toString()}`, {
headers: { "x-api-key": this.apiKey },
});
if (!response.ok) {
throw new Error(await response.text());
}
const body = (await response.json()) as { logs?: LogEntry[] };
return body.logs || [];
}
async fetchInbox(agentId = this.agentId, after = "", limit = 50): Promise<LogEntry[]> {
const params = new URLSearchParams({ agent_id: agentId, limit: String(limit) });
if (after) params.set("after", after);
const response = await fetch(`${this.apiBase}/api/inbox_for_agent?${params.toString()}`, {
headers: { "x-api-key": this.apiKey },
});
if (!response.ok) {
throw new Error(await response.text());
}
const body = (await response.json()) as { messages?: LogEntry[] };
return body.messages || [];
}
async prepareAndSend(
targetAgentId: string,
input: A2spaPayloadContent,
runner: (input: A2spaPayloadContent) => Promise<A2spaPayloadContent> | A2spaPayloadContent,
options: { alertThreshold?: number; stateContinuity?: JsonValue | null } = {},
): Promise<SendResult> {
// The runner must only prepare output. Do not perform side effects before A2SPA authorization.
const output = await runner(input);
return this.sendPayload(targetAgentId, input, output, options);
}
}
async function existingAgent(input: A2spaPayloadContent): Promise<A2spaPayloadContent> {
const message = typeof input === "object" && input && !Array.isArray(input) ? (input as { message?: string }).message : undefined;
return {
status: "ready",
summary: `Processed: ${message ?? "no message"}`,
};
}
async function main(): Promise<void> {
const client = A2SPAClient.fromEnv();
const input = policyInput({ action: "send_message", workflowScope: "messages:send", message: "Hello from my existing TypeScript agent" });
const output = await existingAgent(input);
const requestBody = await client.buildSignedRequest(process.env.A2SPA_TARGET_AGENT_ID || "", input, output);
const response = await client.postRequestBody(requestBody);
console.log(`A2SPA verify response: ${response.status} ${await response.text()}`);
}
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
main().catch((error) => {
console.error(error);
process.exit(1);
});
}
{
"ai_workflow": [
"Create or rotate the needed agents in the authenticated dashboard first.",
"Save the returned private key immediately; it is only shown once.",
"Add the A2SPA environment variables to the target project.",
"Install the published a2spa package and adapt the minimal language example for the target project.",
"Use the inbox_for_agent endpoint to poll received messages for the receiving agent."
],
"api_base": "https://api.aimodularity.com/A2SPA",
"code_generation_rules": [
"Always send signed payloads through POST /api/verify_payload.",
"Put policy-relevant semantics in signed input fields such as intent, action, workflow_scope, amount_usd, currency, and on_behalf_of.",
"When using A2SPA 2.0 state_continuity, register the authoritative state through POST /api/continuity_state first.",
"Always fetch received runtime messages through GET /api/inbox_for_agent.",
"Use GET /api/logs_for_agent for audit and troubleshooting, not as the main runtime inbox.",
"Use the published a2spa package for payload construction and signing where available.",
"Do not verify payloads locally as a substitute for the API.",
"Do not read or write A2SPA backend storage directly from customer agent code.",
"Do not bypass nonce, timestamp, signature, permission, or toggle checks.",
"Store the agent private key outside source control and outside public directories."
],
"discovery_urls": {
"docs_url": "https://aimodularity.com/A2SPA/docs",
"integration_page_url": "https://aimodularity.com/A2SPA/integrations",
"integration_spec_url": "https://aimodularity.com/A2SPA/integrations/spec.json",
"well_known_url": "https://aimodularity.com/A2SPA/.well-known/a2spa-integration.json"
},
"environment_template_url": "https://aimodularity.com/A2SPA/integrations/templates/env",
"must_use_a2spa_api": true,
"package_examples": {
"env": {
"filename": ".env.a2spa.example",
"label": "Environment",
"runtime": "Generic",
"url": "https://aimodularity.com/A2SPA/integrations/templates/env"
},
"javascript": {
"filename": "a2spa-client.mjs",
"label": "JavaScript Package Example",
"runtime": "Node.js 20+",
"url": "https://aimodularity.com/A2SPA/integrations/templates/javascript"
},
"python": {
"filename": "a2spa_client.py",
"label": "Python Package Example",
"runtime": "Python",
"url": "https://aimodularity.com/A2SPA/integrations/templates/python"
},
"typescript": {
"filename": "a2spa-client.ts",
"label": "TypeScript Package Example",
"runtime": "Node.js 20+",
"url": "https://aimodularity.com/A2SPA/integrations/templates/typescript"
}
},
"platform": "A2SPA",
"purpose": "Teach developers and AI coding assistants how to integrate existing agents using the published A2SPA payload helpers and public A2SPA API. Agent setup happens in the authenticated dashboard.",
"runtime_api": {
"continuity_state": {
"headers": {
"Content-Type": "application/json",
"x-api-key": "\u003cdashboard API key\u003e"
},
"method": "POST",
"notes": [
"Use this before verify_payload when a workflow step requires A2SPA 2.0 state-continuity enforcement.",
"The response includes the state_hash to bind into payload.state_continuity.",
"If you enable system-of-record checks, configure the allowed source host/base URL before registering the state."
],
"request_json": {
"binding": {
"action": "reserve",
"agent_id": "\u003csender agent id\u003e",
"amount_usd": "100.00",
"currency": "USD",
"target_agent_id": "\u003creceiver agent id\u003e",
"workflow_scope": "payments:reserve"
},
"expires_at": "2026-08-10T20:00:00Z",
"sequence": 1,
"state": {
"currency": "USD",
"reserved_amount": "100.00",
"status": "reserved"
},
"state_id": "workflow-123-step-1",
"status": "active"
},
"url": "https://api.aimodularity.com/A2SPA/api/continuity_state"
},
"inbox_for_agent": {
"headers": {
"x-api-key": "\u003cdashboard API key\u003e"
},
"method": "GET",
"notes": [
"Use this to poll received messages for an agent through the A2SPA API.",
"This is the preferred runtime retrieval path for receiving agents."
],
"query": {
"after": "optional ISO timestamp for polling",
"agent_id": "\u003cagent id to inspect\u003e",
"limit": 50
},
"url": "https://api.aimodularity.com/A2SPA/api/inbox_for_agent"
},
"logs_for_agent": {
"headers": {
"x-api-key": "\u003cdashboard API key\u003e"
},
"method": "GET",
"notes": [
"Use this for audit logs, troubleshooting, and dashboard-style history.",
"Do not use this as the primary runtime inbox when inbox_for_agent is available."
],
"query": {
"agent_id": "\u003cagent id to inspect\u003e"
},
"url": "https://api.aimodularity.com/A2SPA/api/logs_for_agent"
},
"verify_payload": {
"canonicalization": {
"array_order_preserved": true,
"compute_hash_before_setting_hash_field": true,
"excluded_fields": [
"hash",
"signature",
"extra metadata not in the current verifier schema"
],
"hash_algorithm": "sha256",
"included_fields": [
"agent_id",
"target_agent_id",
"timestamp",
"nonce",
"input",
"output",
"alert_threshold",
"crypto_profile required for current non-legacy profiles",
"state_continuity when present"
],
"number_handling": "Use finite JSON numbers; send exact decimal spellings as strings when cross-language lexical stability matters",
"recursive_key_sorting": true,
"signature_algorithm": "RSA PKCS1v15 SHA256 or ML-DSA-65 when crypto_profile is a2spa.pq.mldsa.v1",
"signing_preimage": "Current v1 signs the UTF-8 bytes of canonical JSON only; no domain-separation prefix is present in this compatibility profile.",
"string_escaping": "Python json.dumps(..., sort_keys=True) with ensure_ascii=True semantics",
"timestamp_format": "ISO-8601 UTC string",
"utf8_bytes": true,
"whitespace": "Python default separators: comma+space and colon+space"
},
"headers": {
"Content-Type": "application/json",
"x-api-key": "\u003cdashboard API key\u003e"
},
"method": "POST",
"request_json": {
"payload": {
"agent_id": "\u003csender agent id\u003e",
"alert_threshold": 10,
"crypto_profile": "a2spa.classical.rsa_pkcs1v15_sha256.v1",
"hash": "sha256 over the UTF-8 bytes of Python-compatible canonical JSON for the signable payload",
"input": {
"action": "reserve",
"message": "hello",
"workflow_scope": "payments:reserve"
},
"nonce": "uuid",
"output": {
"status": "ready"
},
"state_continuity": {
"sequence": 1,
"state_hash": "optional registered state sha256 hex",
"state_id": "workflow-123-step-1"
},
"target_agent_id": "\u003creceiver agent id\u003e",
"timestamp": "ISO-8601 string"
},
"signature": "hex signature over the same canonical UTF-8 bytes using the algorithm selected by crypto_profile"
},
"url": "https://api.aimodularity.com/A2SPA/api/verify_payload"
}
},
"security_model": {
"dashboard_management_source": "A2SPA authenticated dashboard only",
"delegation_boundary": [
"on_behalf_of is rejected unless the sender agent has an explicit delegation policy allowing that principal.",
"Cryptographic binding proves integrity. Policy determines semantic permission.",
"Agent policies can enforce target, sender, intent, action, workflow scope, currency, and spend limits before nonce consumption."
],
"forbidden_shortcuts": [
"Direct backend storage writes",
"Skipping the verify_payload API",
"Embedding private keys in git",
"Replacing server-side verification with local-only checks"
],
"runtime_verification_source": "A2SPA API only"
},
"spec_kind": "api_integration_pack",
"spec_version": "2026-03-23"
}