Request Signing & Security
Every request VocoAgents makes to your endpoints — custom API tools, MCP tools, and webhooks — carries authentication headers so you can confirm the request genuinely came from us, and (optionally) that it is for your account specifically. This lets your endpoint reject anything that is unsigned or unrecognized.
The two headers
They come from two independent systems, so verifying both gives you defense-in-depth: forging a fully valid request would require compromising both, not one.
| Header | Proves | Sent | You verify with |
|---|---|---|---|
x-voco-signature | Genuinely from VocoAgents | Always | Our public key (below) |
x-voco-token | For your account | Only if your account has a secret | A shared secret we gave you privately |
Based on open standards
Both headers are standard JSON Web Tokens — no proprietary format. Any JWT library in any language can verify them. We follow these IETF specifications:
x-voco-signature
A short-lived RS256 JSON Web Token (JWT) signed with our private key. Because it is asymmetric, you verify it with our public key — which can only verify a signature, never create one, so it is safe for us to publish it here. This header is present on every request, and answers "is this genuinely from VocoAgents?" When the agent is connected to an account, it also carries an aud (audience) claim — your account's opaque public id — so you can additionally confirm the request was meant for your account.
Token claims
{
"iss": "vocoagents",
"iat": 1737000000,
"exp": 1737000300, // ~5 minute lifetime
"jti": "unique-token-id", // replay guard (reject a repeated jti to stop replay)
"aud": "account-public-id" // your account's public id — present only when the agent is on an account
}Our public key (RS256)
Copy this into your verifier. It is stable — we do not rotate it.
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/dquG0Gyti3FkSlPOlqj
h5VJAdppsX2lo+WSKvsoD4Emlf4X4r/eC3EBg9iLoZ2c/XQOydNJ9PyaN8kaDvPU
c/99JsdgusfRRiiZahm8eWbFw5Foda3V2Mr62pjYdb+PluhjEh7neSZ+1vBhECgR
qRzANrEGICLhckJHK6dmZg1jkjrzz5Q/qLYs2u7K2gdrruvHq7pLvbo7HHs825MS
6Q1vSxNm9IRnqSQTItyqlmFnn+flCG5pZ9e+cCMmdRiP9z5RwmMGCpl7WH29iEL+
pTo2I23b+P5d8xs5PdOnUn9ImHA4AKU+0ZtlfHXz0/jNxjFtNmEVc2koXnvCkmL2
gwIDAQAB
-----END PUBLIC KEY-----Verify it (Node.js)
// Install: npm i jsonwebtoken
import jwt from 'jsonwebtoken';
const VOCO_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
... paste the public key above ...
-----END PUBLIC KEY-----`;
app.post('/your-endpoint', (req, res) => {
try {
jwt.verify(req.headers['x-voco-signature'], VOCO_PUBLIC_KEY, {
algorithms: ['RS256'],
issuer: 'vocoagents',
audience: 'your-account-public-id', // optional: confirms it was for YOUR account
});
// exp / iss / aud are all checked by the library.
} catch {
return res.status(401).send('bad or missing signature');
}
// ...handle the request
});x-voco-token
A short-lived HS256 JWT signed with a private shared secret, unique to your account, that we hand you directly through your account manager (never over this page or a public channel). The raw secret is never sent — only the signed token is. When your account has a secret, we send this header on every request; you verify the token with the secret and reject anything that fails.
x-voco-token: eyJhbGciOiJIUzI1NiIsImtpZCI6Imtf…<signed JWT>Verify it (Node.js)
Verify with the shared secret we gave you — reject if it fails.
import jwt from 'jsonwebtoken';
try {
jwt.verify(req.headers['x-voco-token'], process.env.VOCO_TOKEN_SECRET, { algorithms: ['HS256'], issuer: 'vocoagents' });
} catch {
return res.status(401).send('bad or missing account token');
}Secret rotation (zero downtime)
When we rotate your secret, no request is ever rejected during the switch:
- We generate a new secret and share it with you — it is not in use yet.
- You add it to your verifier alongside your current secret (matched by the token's
kid), so both are accepted. - We swap over and start signing tokens with only the new secret.
- Once traffic is on the new secret, you remove the old one.
Recommended checks
Verify at least one header — we recommend both.x-voco-signature alone is the quickest to adopt: it needs no onboarding, just our public key (below), and is sent on every request. Verifying x-voco-token as well adds a second, independent layer — the two derive from different mechanisms (a public/private keypair vs. a per-account shared secret held in a separate system), so passing both is meaningfully stronger than either alone.
- Verify
x-voco-signatureagainst our public key, and reject on failure. - Confirm the token is unexpired (the library checks
expfor you). - For the strongest posture, also verify
x-voco-tokenwith your shared secret (HS256) and require it. - Always serve your endpoints over HTTPS.
