Skip to main content
This guide shows how to create an Argo Events EventSource that exposes an HTTP webhook, how the webhook maps to CloudEvents-style payloads, and how to verify the EventSource and Service inside your Kubernetes cluster. Use this tutorial to receive webhooks from external systems (GitHub, GitLab, CI/CD tools, custom services) and forward them into Argo’s eventing pipeline. Argo Events supports many sources (AWS SNS, SQS, Azure services, calendar events, GCP Pub/Sub, GitHub, GitLab, Bitbucket, MinIO, Kafka, and more). For this demo we use the webhook EventSource, which runs an HTTP server and transforms incoming requests into CloudEvents-style envelopes.

CloudEvents envelope examples

A typical CloudEvents-style envelope produced by Argo Events contains a context and data section:
{
  "context": {
    "type": "type_of_event_source",
    "specversion": "cloud_events_version",
    "source": "name_of_the_event_source",
    "id": "unique_event_id",
    "time": "event_time",
    "datacontenttype": "type_of_data",
    "subject": "name_of_the_configuration_within_event_source"
  },
  "data": {
    "eventTime": "2025-10-25T10:45:36Z",
    "userPayload": { /* static payload available in the event source */ }
  }
}
When the webhook receives an HTTP request, the event payload typically includes the request headers and body:
{
  "context": {
    "type": "type_of_event_source",
    "specversion": "cloud_events_version",
    "source": "name_of_the_event_source",
    "id": "unique_event_id",
    "time": "event_time",
    "datacontenttype": "type_of_data",
    "subject": "name_of_the_configuration_within_event_source"
  },
  "data": {
    "header": { /* headers from the received HTTP request */ },
    "body": { /* payload received in the HTTP request body */ }
  }
}
Note: the exact structure may vary depending on how the EventSource is configured (e.g., static payloads, custom converters, or secret mappings).
The webhook EventSource listens on the configured port and endpoint. By default, Argo Events will create a Kubernetes Service that exposes the same port. If you want traffic from outside the cluster to reach the webhook, expose the Service using a suitable type for your environment (LoadBalancer, NodePort, Ingress, etc.).

Prerequisites

  • A Kubernetes cluster with kubectl configured to access it.
  • Argo Events (controller, eventbus, etc.) installed into a namespace (commonly argo-events).
  • Optional: an Ingress or LoadBalancer if you need external, public endpoints.

Install Argo Events (if needed)

Apply the upstream manifests to install Argo Events and an example Sensor. Adjust URLs if you use local manifests:
kubectl apply -n argo-events -f https://raw.githubusercontent.com/argoproj/argo-events/master/manifests/install.yaml
kubectl apply -n argo-events -f https://raw.githubusercontent.com/argoproj/argo-events/master/examples/sensor.yaml

Example EventSource manifest (webhook)

Below is an example EventSource that creates:
  • A webhook listener on port 13000.
  • An endpoint at /push which accepts only POST requests.
  • A generated ClusterIP Service exposing port 13000.
apiVersion: argoproj.io/v1alpha1
kind: EventSource
metadata:
  name: webhook
  namespace: argo-events
spec:
  service:
    ports:
      - port: 13000
        targetPort: 13000
  webhook:
    my-webhook:
      port: "13000"
      endpoint: /push
      method: "POST"
Key fields explained:
FieldPurposeExample
spec.service.ports.portPort exposed by the generated Kubernetes Service13000
spec.webhook.<name>.portPort the webhook server listens on (string)“13000”
spec.webhook.<name>.endpointHTTP path to accept events on/push
spec.webhook.<name>.methodRestrict allowed HTTP methods (POST, GET, etc.)”POST”
Apply the manifest (for example save as webhook-eventsource.yaml):
kubectl apply -f webhook-eventsource.yaml
If you created the EventSource via the Argo UI, the same resources are created in the selected namespace.

Verify the EventSource and Service

List resources in the argo-events namespace to confirm the EventSource pod and Service are running:
kubectl get all -n argo-events
Example output (trimmed for clarity):
NAME                                             READY   STATUS    RESTARTS   AGE
pod/controller-manager-59884fd695-kt5gm         1/1     Running   0          16m
pod/eventbus-default-stan-0                     2/2     Running   0          9m
pod/eventbus-default-stan-1                     2/2     Running   0          9m
pod/eventbus-default-stan-2                     2/2     Running   0          9m
pod/webhook-eventsource-8jhxz-b99f96879-nw6pb   1/1     Running   0          24s

NAME                                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)           AGE
service/eventbus-default-stan-svc      ClusterIP   None             <none>        4222/TCP,...      9m
service/webhook-eventsource-svc        ClusterIP   10.104.178.99    <none>        13000/TCP         24s

NAME                                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/controller-manager          1/1     1            1           16m
deployment.apps/webhook-eventsource-8jhxz   1/1     1            1           24s

NAME                                                   DESIRED   CURRENT   READY   AGE
replicaset.apps/controller-manager-59884fd695          1         1         1       16m
replicaset.apps/webhook-eventsource-8jhxz-b99f96879    1         1         1       24s

NAME                                      READY   AGE
statefulset.apps/eventbus-default-stan    3/3     9m
This confirms:
  • The webhook EventSource pod is running.
  • A ClusterIP Service (service/webhook-eventsource-svc) exposes port 13000.
Additional useful commands:
  • Inspect the EventSource resource:
    kubectl -n argo-events get eventsources webhook -o yaml
    
  • View cluster events:
    kubectl -n argo-events get events
    

Test the webhook locally (port-forward)

If you don’t have a LoadBalancer or Ingress, port-forward to the Service/pod and test with curl:
  1. Port-forward the Service (or pod) to localhost:
    kubectl -n argo-events port-forward svc/webhook-eventsource-svc 13000:13000
    
  2. In another terminal, POST a test payload:
    curl -X POST http://localhost:13000/push \
      -H "Content-Type: application/json" \
      -d '{"message":"hello from curl"}'
    
  3. Check the EventSource logs to see the incoming request being wrapped into a CloudEvents-style envelope:
    kubectl -n argo-events logs -l app=webhook-eventsource
    
Replace the label selector with the exact Pod/Deployment name if needed.

How the flow works

  • External systems POST to http://<cluster-ip-or-loadbalancer>:13000/push (or your public ingress URL).
  • The EventSource receives the request, wraps it into a CloudEvents-like envelope (context + data), and forwards the event onto the configured EventBus.
  • Sensors in Argo Events consume these events and trigger Argo Workflows, notifications, or other actions.
In the Argo UI (Event Flow or Event Sources view) you should see the new “my-webhook” event source after creation.

Quick troubleshooting

SymptomCheck
No Service on expected portkubectl -n argo-events get svc - check service/webhook-eventsource-svc ports
POST returns 404Confirm endpoint path (endpoint: /push) and HTTP method allowed
Events not triggering SensorsCheck EventBus health and Sensor configurations in the same namespace
External webhooks can’t reach clusterVerify Ingress/LoadBalancer, firewall rules, and DNS routing
If you expose a webhook endpoint to the public internet, secure it: use TLS, require authentication or tokens, validate payloads, and restrict source IPs where possible. An unprotected webhook can be abused or flood your cluster with requests.
Use this pattern as a base to extend the webhook EventSource with authentication, custom converters, static payloads, or multiple endpoints per EventSource.

Watch Video