Skip to main content

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

MethodPathPurpose
GET/v1/manifestAgent capabilities, task types, input schemas, required credentials
GET/v1/healthLiveness check — returns build SHA, protocol version, and status
POST/v1/taskStart an async task → 202 Accepted immediately
POST/v1/invokeSynchronous short action (must respond in < 10s) → 200 with result
POST/v1/session/:id/messageOne turn in a multi-turn conversation
POST/v1/approval/:id/resolveCore 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 typeWhen to emit
task.progressDuring execution — percentage complete and a human-readable status message
task.completeTask finished successfully — include all produced artifacts
task.failedTask failed — include error code and message
approval.requestedAgent wants to take an external action — Core holds it until human resolves
artifact.emittedAn artifact was produced (can be emitted before task.complete for streaming)
telemetry.spanOpenTelemetry-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