Remote Agent Protocol (RAP v1)
RAP v1 is the open HTTP contract every Ariftly agent must implement. It is language-agnostic and versioned — any HTTP service that exposes these endpoints can be registered as an Ariftly agent.
This means you can build agents in Python, Node.js, Go, Rust, or any language that can serve HTTP. If it can respond to a POST request, it can be an Ariftly agent.
Required Endpoints
| Method | Path | Purpose |
|---|---|---|
GET | /v1/manifest | Agent capabilities, task types, input schemas, required credentials |
GET | /v1/health | Liveness check — returns build SHA, protocol version, and status |
POST | /v1/task | Start an async task → 202 Accepted immediately |
POST | /v1/invoke | Synchronous short action (must respond in < 10s) → 200 with result |
POST | /v1/session/:id/message | One turn in a multi-turn conversation |
POST | /v1/approval/:id/resolve | Core notifies the agent that a human approved or denied a pending action |
All endpoints must be HTTPS in production. HTTP is accepted only on localhost during development.
Task Trigger Request
When Core dispatches a task to your agent, it sends a signed TaskTriggerRequest to /v1/task:
{
"wire_version": "1.0",
"task_id": "task_xyz789",
"task_type": "ai_readiness.full_audit",
"tenant_id": "tenant_abc",
"input": {
"scope": "full",
"focus_framework": "eu_ai_act"
},
"callback": {
"url": "https://core.ariftly.io/api/v1/agent-events",
"hmac_key_id": "key_001"
},
"credentials": {
"ai_gateway": {
"base_url": "https://ai-gateway.ariftly.io/v1",
"token": "gw_..."
},
"integrations": {
"github": { "token": "ghs_proxy_..." }
}
},
"tool_proxy": {
"base_url": "https://core.ariftly.io/api/v1/tools",
"allowed_tools": ["github.list_repos", "github.get_file_content", "github.create_pr"]
}
}
Your agent should return 202 Accepted immediately and process the task asynchronously. Long-running tasks emit progress events back to the callback URL while running.
Security note: Agents receive proxy tokens, never raw integration credentials. The proxy token grants access only to the specific tools listed in tool_proxy.allowed_tools and expires when the task completes.
Emitting Events Back to Core
Agents emit events to callback.url using HMAC-signed POST requests. Sign every request with HMAC-SHA256 using the key identified by callback.hmac_key_id:
# Computing the signature
BODY='{"event_type":"task.progress",...}'
SIGNATURE=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$HMAC_SECRET" -binary | base64)
curl -X POST https://core.ariftly.io/api/v1/agent-events \
-H "Content-Type: application/json" \
-H "X-Ariftly-Signature: sha256=$SIGNATURE" \
-H "X-Ariftly-Key-ID: key_001" \
-d "$BODY"
Event types
| Event type | When to emit |
|---|---|
task.progress | During execution — percentage complete and a human-readable status message |
task.complete | Task finished successfully — include all produced artifacts |
task.failed | Task failed — include error code and message |
approval.requested | Agent wants to take an external action — Core holds it until human resolves |
artifact.emitted | An artifact was produced (can be emitted before task.complete for streaming) |
telemetry.span | OpenTelemetry-compatible span for distributed tracing |
Task completion event
{
"event_type": "task.complete",
"task_id": "task_xyz789",
"sequence": 5,
"payload": {
"artifacts": [
{
"type": "ai_readiness.audit_report",
"data": {
"gaps_found": 14,
"critical": 3,
"eu_ai_act_score": 62,
"recommendations": ["..."]
}
}
]
}
}
Approval request event
{
"event_type": "approval.requested",
"task_id": "task_xyz789",
"sequence": 3,
"payload": {
"approval_type": "send_email",
"action": {
"to": "alex@acme.com",
"subject": "AI Readiness Platform — quick intro",
"body": "Hi Alex..."
},
"context": "Lead identified from GitHub activity: acme/ml-pipeline has 3 recent AI-related commits"
}
}
Agent Manifest
The manifest is how Core discovers what your agent can do. Return it from GET /v1/manifest:
{
"slug": "ai-readiness-agent",
"name": "AI Readiness Agent",
"version": "1.0.0",
"wire_version": "1.0",
"description": "EU AI Act compliance auditing, gap analysis, and procurement questionnaire response drafting",
"task_types": [
{
"type": "ai_readiness.full_audit",
"description": "Full EU AI Act compliance gap analysis",
"input_schema": {
"type": "object",
"properties": {
"scope": { "type": "string", "enum": ["full", "delta"] },
"focus_framework": { "type": "string", "enum": ["eu_ai_act", "nist_ai_rmf", "both"] }
},
"required": ["scope"]
}
},
{
"type": "ai_readiness.questionnaire_response",
"description": "Draft responses to a procurement questionnaire",
"input_schema": {
"type": "object",
"properties": {
"questionnaire_text": { "type": "string" }
},
"required": ["questionnaire_text"]
}
}
],
"artifact_types": [
"ai_readiness.audit_report",
"ai_readiness.questionnaire_response",
"ai_readiness.gap_summary"
],
"required_credentials": [
{ "provider": "github", "kind": "oauth2" }
],
"approval_types": [
"send_email"
]
}
Health check
Return from GET /v1/health:
{
"status": "ok",
"wire_version": "1.0",
"build_sha": "a3f7c21",
"agent_version": "1.0.0",
"uptime_seconds": 86400
}
Core calls /v1/health every 60 seconds to verify your agent is running. If health checks fail for 5 consecutive minutes, the agent is marked as Unavailable in the dashboard and no new tasks are dispatched.
Building a minimal agent (Python example)
from fastapi import FastAPI, Request, BackgroundTasks
import httpx, hmac, hashlib, json, os
app = FastAPI()
HMAC_SECRET = os.environ["ARIFTLY_HMAC_SECRET"]
def sign(body: str) -> str:
sig = hmac.new(HMAC_SECRET.encode(), body.encode(), hashlib.sha256).digest()
return "sha256=" + sig.hex()
async def run_task(task: dict):
task_id = task["task_id"]
callback_url = task["callback"]["url"]
# ... do the actual work ...
result = {"gaps_found": 14, "critical": 3, "eu_ai_act_score": 62}
body = json.dumps({
"event_type": "task.complete",
"task_id": task_id,
"sequence": 1,
"payload": {"artifacts": [{"type": "ai_readiness.audit_report", "data": result}]}
})
async with httpx.AsyncClient() as client:
await client.post(callback_url, content=body, headers={
"Content-Type": "application/json",
"X-Ariftly-Signature": sign(body),
"X-Ariftly-Key-ID": task["callback"]["hmac_key_id"]
})
@app.get("/v1/manifest")
def manifest():
return {
"slug": "my-custom-agent",
"name": "My Custom Agent",
"version": "1.0.0",
"wire_version": "1.0",
"task_types": [{"type": "my_agent.do_work", "input_schema": {}}],
"artifact_types": ["my_agent.result"],
"required_credentials": []
}
@app.get("/v1/health")
def health():
return {"status": "ok", "wire_version": "1.0"}
@app.post("/v1/task")
async def task(request: Request, background_tasks: BackgroundTasks):
payload = await request.json()
background_tasks.add_task(run_task, payload)
return {"accepted": True}, 202
Registering your agent with Core
Once your agent is deployed and accessible over HTTPS:
curl -X POST https://api.ariftly.io/v1/agents \
-H "Authorization: Bearer $ARIFTLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"base_url": "https://my-agent.example.com",
"name": "My Custom Agent"
}'
Core fetches /v1/manifest and /v1/health immediately to validate the registration.
Protocol versioning
The current wire version is 1.0. Breaking changes will increment to 2.0 with a 12-month deprecation period. Non-breaking additions are added in minor versions (e.g., new event types). Your agent declares the wire version it supports in the manifest and in every event it emits.
What's next
- Tasks & Artifacts — task lifecycle and artifact schema reference
- Authentication — HMAC webhook verification and API key scopes
- Approvals & Skills — how the approval protocol works end-to-end