Skip to Content
IntegrationsAWS EventBridge

AWS EventBridge

Set Convoy as a trigger in your AWS account: when a cargo completes, Convoy publishes an event onto your EventBridge event bus, and your rules fan it out to anything — Lambda, Step Functions, SQS, SNS, ECS, API destinations. Convoy owns one PutEvents call; everything downstream is yours.

your code ── POST /cargo/load ──▶ Convoy ──▶ …batch processing… │ ▼ cargo completes AssumeRole(your role, ExternalId) │ ▼ events:PutEvents(Source: "convoy.cargo", DetailType: "Cargo Completed" | "Cargo Failed", Detail: <result envelope>) │ ▼ your rules on the bus match & route to N targets { "source": ["convoy.cargo"], "detail-type": ["Cargo Completed"] }

Why EventBridge is the best “trigger” story of the direct-AWS targets:

  • Fan-out and filtering are yours — one cargo completion can trigger N targets, filtered on any envelope field via detail.… content patterns (including your own detail.metadata.… keys, since Convoy echoes your submit-time metadata verbatim).
  • Retries and DLQs are yours — rule-target delivery retries and dead-letter queues are native EventBridge features.
  • Loosely coupled — adding a new consumer needs zero Convoy-side change: just another rule.

Event shape

FieldValue
sourceconvoy.cargo (self-hosted deployments may override via the worker’s CONVOY_EVENTBRIDGE_SOURCE env)
detail-typeCargo Completed on success, Cargo Failed on failure
detailthe standard result envelope

Success and failure are split across detail-type so rules can match failures without content filters.

Setup

Create an AWS connection

Same connection as the other direct-AWS targets — see AWS connections. One connection can carry every capability.

Grant events:PutEvents on your role

Scope it to your exact bus — never Resource: "*":

{ "Effect": "Allow", "Action": "events:PutEvents", "Resource": "arn:aws:events:us-east-1:123456789012:event-bus/my-bus" }

Or use the CloudFormation role template  with the EventBusArns parameter. Each delivery’s STS session additionally carries a session policy pinned to the ONE bus the cargo named.

Verify the capability (optional)

In the dashboard, open your AWS connection and run the EventBridge capability check with your event bus ARN. EventBridge has no dry-run mode, so the probe publishes one real marked event to your bus.

The probe event has source: "convoy.verify" and detail-type: "Convoy Verification" — your rules on source = convoy.cargo won’t match it. The probe is rate-capped at 1/minute per connection, and it’s opt-in: you can skip it and rely on fail-at-delivery instead.

Submit cargo with an aws_eventbridge callback

curl -X POST https://api.cnvy.ai/cargo/load \ -H "X-API-Key: $CONVOY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "params": { "model": "claude-haiku-4-5", "max_tokens": 1024, "messages": [{"role": "user", "content": "Summarize this document…"}] }, "callback": { "type": "aws_eventbridge", "connection_id": "awsconn_…", "event_bus_arn": "arn:aws:events:us-east-1:123456789012:event-bus/my-bus" }, "metadata": {"tenant": "acme", "job": "digest-42"} }'

The bus must be in the connection’s region. When the connection carries a target allow-list, the bus ARN must be listed on it.

Complete example: bus + rule + Lambda + DLQ

A minimal SAM/CloudFormation snippet showing the customer-side pieces — a bus, a rule matching Convoy’s completions, a Lambda target, and a DLQ for failed rule deliveries:

Resources: CargoBus: Type: AWS::Events::EventBus Properties: Name: my-bus CargoCompletedRule: Type: AWS::Events::Rule Properties: EventBusName: !Ref CargoBus EventPattern: source: ["convoy.cargo"] detail-type: ["Cargo Completed"] Targets: - Id: handler Arn: !GetAtt OnCargoDone.Arn DeadLetterConfig: Arn: !GetAtt RuleDLQ.Arn RetryPolicy: MaximumRetryAttempts: 8 MaximumEventAgeInSeconds: 3600 # Route failures to a second consumer without touching Convoy. # FailureTopic must be an EXISTING SNS topic ARN you own — declare it # elsewhere (or add an AWS::SNS::Topic resource here) and reference it: CargoFailedRule: Type: AWS::Events::Rule Properties: EventBusName: !Ref CargoBus EventPattern: source: ["convoy.cargo"] detail-type: ["Cargo Failed"] Targets: - Id: alerts Arn: !Ref FailureTopic RuleDLQ: Type: AWS::SQS::Queue OnCargoDone: Type: AWS::Serverless::Function Properties: Handler: handler.lambda_handler Runtime: python3.12

Route on your own metadata with a content filter:

{ "source": ["convoy.cargo"], "detail-type": ["Cargo Completed"], "detail": { "metadata": { "tenant": ["acme"] } } }

The result envelope

Your rule targets receive the standard envelope in detail:

FieldDescription
cargo_idThe cargo this result belongs to — dedupe key
successtrue / false (also reflected in detail-type)
responseFull model response (or null when truncated/failed)
response_truncatedtrue when the response was too large to inline
result_urlGET /cargo/{cargo_id}/result — fetch the full result
error_messagePresent on failure events only
metadataYour submit-time metadata, echoed verbatim

Delivery is at-least-once: a rare lost response inside Convoy’s retry window can produce a duplicate event. Dedupe on detail.cargo_id in your consumers — idiomatic for EventBridge consumers anyway.

Troubleshooting

SymptomCause / fix
region_mismatch (422 at submit)The bus is in a different region than the connection. Create a connection in the bus’s region.
target_not_allowed (422 at submit)The connection carries a target allow-list and this bus ARN isn’t on it.
target_not_found in the DLQThe bus was deleted after submit.
assume_role_denied + connection flagged failedThe role trust policy or the events:PutEvents grant was removed. Fix the role and re-verify.
Events published but nothing firesCheck your rule’s EventBusName and pattern — remember source is convoy.cargo and detail-type splits Completed/Failed.
Last updated on