Integrations
Custom Webhooks
Connect any system that can POST JSON with generic HMAC or Bearer authentication.
Use custom webhooks when a system can POST JSON but AgentField does not ship a named source for it.
This is the right path for internal services, simple SaaS webhooks, and tools such as Zapier. Zapier is not a native AgentField OSS integration; it can connect by posting to one of these generic sources.
Source Surfaces
| Source | Auth | Config | Event type | Idempotency |
|---|---|---|---|---|
generic_hmac | HMAC-SHA256 over the raw body. | signature_header, optional signature_prefix, event_type_header, idempotency_header. | Optional header named by event_type_header. | Optional header named by idempotency_header. |
generic_bearer | Constant-time comparison against a bearer token. | header, scheme, optional event_type_header, idempotency_header. | Optional header named by event_type_header. | Optional header named by idempotency_header. |
Neither source ships a separate capability node. Put provider-specific behavior in your reasoner.
Generic HMAC
generic_hmac verifies an HMAC-SHA256 signature over the raw request body.
from agentfield import Agent, on_event
app = Agent(node_id="webhook-agent")
@app.reasoner()
@on_event(source="generic_hmac", secret_env="CUSTOM_WEBHOOK_SECRET")
async def handle_signed_webhook(event: dict, trigger=None):
return {"event_type": trigger.event_type if trigger else "webhook"}
app.run()import { Agent } from '@agentfield/sdk';
const agent = new Agent({ nodeId: 'webhook-agent' });
// After signature verification, the control plane dispatches the normalized
// payload as ctx.input.event with trigger metadata in ctx.input._meta.
// The TypeScript SDK has no declarative source binding — connect the
// `generic_hmac` source to this reasoner from the UI (or trigger REST API).
agent.reasoner('handleSignedWebhook', async (ctx) => {
const meta = ctx.input._meta;
return { event_type: meta?.event_type ?? 'webhook' };
});
agent.serve();a, _ := agent.New(agent.Config{NodeID: "webhook-agent", Version: "1.0.0"})
// Declare the generic_hmac binding in code. The control plane verifies the
// HMAC-SHA256 digest with CUSTOM_WEBHOOK_SECRET, then dispatches the
// normalized payload as input["event"] with metadata in input["_meta"].
a.RegisterReasoner("handle_signed_webhook", func(ctx context.Context, input map[string]any) (any, error) {
meta, _ := input["_meta"].(map[string]any)
eventType := "webhook"
if meta != nil {
if et, ok := meta["event_type"].(string); ok {
eventType = et
}
}
return map[string]any{"event_type": eventType}, nil
},
agent.WithEventTrigger("generic_hmac"),
agent.WithTriggerSecretEnv("CUSTOM_WEBHOOK_SECRET"),
)
a.Serve(context.Background())Config:
{
"signature_header": "X-Signature",
"signature_prefix": "sha256=",
"event_type_header": "X-Event-Type",
"idempotency_header": "X-Delivery-ID"
}Generic Bearer
generic_bearer compares a token in a request header with the secret value from the configured env var.
{
"header": "Authorization",
"scheme": "Bearer",
"event_type_header": "X-Event-Type",
"idempotency_header": "X-Delivery-ID"
}Dispatch Contract
Both generic sources dispatch the request body as the normalized event:
{
"event": {
"incident_id": "INC-123",
"severity": "high"
},
"_meta": {
"source": "generic_hmac",
"event_type": "incident.created",
"idempotency_key": "delivery-123"
}
}Notes
- Use HMAC when the sender can sign payloads.
- Use Bearer only for trusted systems that can protect a static token.
- Set an idempotency header when the sender has a stable delivery ID.
- Keep provider-specific behavior in your reasoner, not in a fake named integration.