Everything you need to query and publish Business Context Manifests.
Query any company's BCM with a single GET request. No API key required.
AI agents discover BCMs using a two-step lookup, similar to how crawlers check robots.txt:
Self-hosting your BCM at the well-known path is the canonical method. The registry serves as a fallback for companies that haven't self-hosted yet.
GET /.well-known/bcm/{domain}.jsonReturns all published BCMs for a domain as a single merged JSON file. Hosted by the registry at bcmspec.org.
GET /api/bcm/{domain}/{transaction_type}| domain | string | Company domain (e.g. acme.com) |
| transaction_type | string | One of the supported transaction types |
| X-Cache | HIT or MISS |
| X-BCM-Version | BCM spec version |
SDKs and agents resolve BCMs using a two-step discovery protocol:
Check the company's own domain first
https://{domain}/.well-known/bcm.jsonFall back to the central registry
https://bcmspec.org/.well-known/bcm/{domain}.jsonCompanies can self-host by placing a bcm.json file at their /.well-known/ path. If not found, SDKs automatically query the BCM Registry.
GET /.well-known/bcm/{domain}.jsonThis is the same format a company would self-host at https://{domain}/.well-known/bcm.json
{
"domain": "acme.com",
"transaction_type": "invoice_submission",
"bcm": {
"transaction_type": "invoice_submission",
"version": "1.0",
"domain": "acme.com",
"last_updated": "2025-01-15",
"accepted_formats": ["PDF", "UBL 2.1"],
"required_fields": [
"po_number",
"invoice_date",
"line_items",
"amount",
"currency"
],
"authority_limits": {
"auto_approve_usd": 5000,
"requires_manager_approval_usd": 25000
},
"compliance_requirements": [
"W9_on_file",
"MSA_signed"
],
"submission_endpoint": {
"email": "ap@acme.com",
"preferred_channel": "email"
},
"response_sla": {
"acknowledgment_hours": 4,
"processing_hours": 48
},
"escalation_path": {
"contact_email": "procurement@acme.com",
"contact_role": "AP Manager"
},
"data_residency": "US"
},
"meta": {
"verified": true,
"last_updated": "2025-01-15T00:00:00.000Z",
"version": 3
}
}The full BCM JSON Schema is available at:
Source: BusinessContextManifest/bcm-spec
BCM queries are free and open — no API key required. Agents can optionally register to get higher rate limits and query analytics.
Agent keys use the format bcm_live_ followed by 32 hex characters (128 bits of entropy). Keys are SHA-256 hashed before storage.
The BCM query endpoint enforces 60 requests per minute per IP. Exceeding this returns 429 Too Many Requests with a Retry-After header indicating seconds until the window resets.
Every sensitive operation is recorded in an append-only audit log. Logged events include: BCM publish/edit, API key creation/deletion, agent blocking, domain verification, and authentication attempts.
Each entry captures: event type, actor, target, IP address, user agent, and timestamp. Audit logs are visible in the company dashboard and cannot be deleted.
Webhook payloads are signed with HMAC-SHA256 using your webhook secret. The signature is sent in the X-BCM-Signature header as sha256=<hex>.
Every payload includes a timestamp field. Reject events older than 5 minutes to prevent replay attacks.
const crypto = require('crypto');
function verifyWebhook(body, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
const sig = signature.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(sig)
);
}
// Express example
app.post('/webhooks/bcm', (req, res) => {
const raw = JSON.stringify(req.body);
const sig = req.headers['x-bcm-signature'];
if (!verifyWebhook(raw, sig, process.env.BCM_WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Check timestamp to prevent replay
const age = Date.now() - new Date(req.body.timestamp).getTime();
if (age > 5 * 60 * 1000) {
return res.status(400).send('Event too old');
}
console.log('Event:', req.body.event, req.body.data);
res.status(200).send('OK');
});import hmac, hashlib, json, time
from datetime import datetime
def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(), body, hashlib.sha256
).hexdigest()
sig = signature.replace('sha256=', '')
return hmac.compare_digest(expected, sig)
# Flask example
@app.route('/webhooks/bcm', methods=['POST'])
def handle_webhook():
raw = request.get_data()
sig = request.headers.get('X-BCM-Signature', '')
if not verify_webhook(raw, sig, os.environ['BCM_WEBHOOK_SECRET']):
return 'Invalid signature', 401
# Check timestamp to prevent replay
payload = json.loads(raw)
event_time = datetime.fromisoformat(payload['timestamp'].replace('Z', '+00:00'))
age = (datetime.now(event_time.tzinfo) - event_time).total_seconds()
if age > 300:
return 'Event too old', 400
print(f"Event: {payload['event']}", payload['data'])
return 'OK', 200All API responses include Access-Control-Allow-Origin: *
Responses are cached for 5 minutes at the edge. The X-Cache header indicates cache status.
Free forever. Takes 2 minutes.
List your company free