Ship a tool that any agent on agnt8x can use. Deploy your code anywhere — Railway, Vercel, AWS, your own server. The platform forwards agent calls to your endpoint and returns your response back to the agent.
Each agnt8x app is a single HTTP endpoint that takes a JSON payload and returns a JSON response. Builders deploy their endpoint, then submit the app via Forge with the Invoke URL filled in. From that point on, any agent bound to the app calls your endpoint when it needs to use the tool.
When an agent invokes your app, agnt8x sends:
| Method | POST |
|---|---|
| URL | Your invoke_url |
| Headers |
Content-Type: application/jsonUser-Agent: agnt8x-platform/1.0 (+https://agnt8x.ai)X-Agnt8x-Signature: sha256=<hex> — HMAC of the raw body using your invoke_secretX-Agnt8x-Library-Id: <uuid>X-Agnt8x-Caller: <user_uuid>
|
| Body | JSON — the input the agent constructed for your tool, matching the schema you declared on submit |
Your endpoint must:
200 and a JSON body on successX-Agnt8x-Signature header before processing (see below)The platform also sends { "ping": true } to your endpoint when the builder clicks "Test reachability". Treat ping requests specially and return { "pong": true }.
// server.js — DocExtract-style app
import express from "express";
import crypto from "node:crypto";
const app = express();
app.use(express.json({ limit: "10mb" }));
const INVOKE_SECRET = process.env.AGNT8X_INVOKE_SECRET;
// Middleware: verify the signature
app.use((req, res, next) => {
const sig = req.header("X-Agnt8x-Signature") || "";
const body = JSON.stringify(req.body);
const expected = "sha256=" + crypto
.createHmac("sha256", INVOKE_SECRET)
.update(body)
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
return res.status(401).json({ error: "Invalid signature" });
}
next();
});
// Your tool's logic
app.post("/", async (req, res) => {
// Ping check
if (req.body.ping === true) {
return res.json({ pong: true, version: "1.0.0" });
}
// Your actual work here
const { document_text } = req.body;
// ... process ...
res.json({
extracted: { vendor: "Acme Corp", total: 1250.00 },
confidence: { vendor: 0.98, total: 0.99 }
});
});
app.listen(process.env.PORT || 3000);
AGNT8X_INVOKE_SECRET to the secret returned at submit time, and paste your URL
into the Forge submit form. Your app is live.
Every request the platform sends to your endpoint carries an
X-Agnt8x-Signature header. This is the HMAC-SHA256 of the
raw request body, signed with your invoke_secret (returned
once when you submitted the app — save it).
Verification in Node:
const expected = "sha256=" + crypto
.createHmac("sha256", INVOKE_SECRET)
.update(rawRequestBody)
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(receivedHeader), Buffer.from(expected))) {
return res.status(401).end();
}
In Python:
import hmac, hashlib
expected = "sha256=" + hmac.new(
INVOKE_SECRET.encode(), raw_body, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, received_header):
return 401
When you submit the app, two fields tell the agent how to use your tool:
input_params |
What fields your endpoint expects. Accepts JSON Schema, or a CSV shorthand like
email, name (required), age. The agent will conform to this schema. |
|---|---|
output_type |
What you return. Free-text description of the JSON shape, e.g.
{ extracted: {...}, confidence: {...0-1 per field} } |
The more precise these are, the better the agent calls your tool. Vague schemas get vague usage.
After submitting, click Test reachability in your app's row in Forge. The platform
POSTs { "ping": true } to your endpoint with a valid signature header. Your endpoint should
return any 2xx response — convention is { "pong": true }.
Direct invocation for end-to-end testing (any signed-in user):
curl -X POST https://api.eightx.app/api/v1/library/<your-app-id>/invoke \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your-jwt>" \
-d '{"document_text": "Acme Corp invoice for $1,250.00"}'
This goes through all the gates (auth, subscription check, rate limit) just like an agent call would.
| Request timeout | 60 seconds (hard cap) |
|---|---|
| Request body size | 10 MB max |
| Response body size | 1 MB max (anything larger gets truncated and returned as an error) |
| Rate limit (default) | 60 calls/minute per (user, app) — adjustable up to 1000 at submit |
| Input schema size | 60 fields max |
If your tool needs longer than 60s, restructure: return a job_id and offer a polling endpoint (V2 pattern, not yet wired into agents — pure functions only in V1).
V1 does not forward the user's OAuth tokens from connectors (Google, HubSpot, Slack, etc.). If your app needs to read or write to a user's third-party account, use the BYOK pattern:
X-Agnt8x-Caller.Eventually (V2), the platform will offer to forward connector tokens directly. Until then, BYOK is the pattern for cross-service apps.
Ready? Open Forge → Applications, click Submit new app, fill in:
https://docextract-prod.up.railway.app/extractThe platform returns your invoke_secret once on submission — save it. You'll need it to verify request signatures from the platform.