Skip to content
Integrations
Integrations

Custom Webhooks

Connect any system that can POST JSON with generic HMAC or Bearer authentication.

Generic HMAC connect dialog for a custom webhook trigger

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

SourceAuthConfigEvent typeIdempotency
generic_hmacHMAC-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_bearerConstant-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.