Triggering Deployed Workflows
Once you've deployed a workflow with an HTTP trigger, you can execute it by sending authenticated HTTP requests to the CRE gateway. This guide explains how to trigger deployed workflows in production.
What you'll learn
This guide covers the complete technical specification for triggering deployed workflows:
- Request format - The JSON-RPC structure for workflow execution requests
- JWT authentication - How to generate cryptographically signed tokens
- Signature process - The ECDSA signing steps for Ethereum-compatible authentication
- Reference implementations - Code examples in Go and TypeScript
Prerequisites
- Deployed workflow: Your workflow must be deployed with an HTTP trigger. See Deploying Workflows.
- Workflow ID: Available from deployment output or the CRE UI.
- Private key: The private key corresponding to one of the
authorizedKeysconfigured in your HTTP trigger.
Finding your workflow ID
Your workflow ID is a 64-character hexadecimal string (without 0x prefix) that uniquely identifies your deployed workflow.
From deployment output
When you deploy a workflow, the CLI displays the workflow ID:
$ cre workflow deploy my-workflow --target production-settings
...
Details:
Workflow ID: a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890
...
From the CRE UI
- Log in to cre.chain.link/workflows
- Click on your workflow name
- In the Overview section, find the Workflow ID field
- Click the copy button to copy it to your clipboard
Request format
All workflow executions use JSON-RPC 2.0 format:
POST https://01.gateway.zone-a.cre.chain.link
Content-Type: application/json
Authorization: Bearer <JWT_TOKEN>
{
"id": "unique-request-id",
"jsonrpc": "2.0",
"method": "workflows.execute",
"params": {
"input": {
"key1": "value1",
"key2": "value2"
},
"workflow": {
"workflowID": "your-64-character-workflow-id"
}
}
}
Request components
| Field | Description |
|---|---|
jsonrpc | Always "2.0" (JSON-RPC version) |
id | Unique identifier for this request (any string, used for tracking) |
method | Always "workflows.execute" |
params.input | Your custom JSON payload (passed to your workflow callback) |
params.workflow.workflowID | Your 64-character workflow ID (no 0x prefix) |
JWT authentication
The Authorization header must contain a Bearer JWT (JSON Web Token) that proves the request was signed by an authorized key. The JWT has three parts: header.payload.signature.
JWT structure
The JWT is a base64url-encoded string consisting of three parts separated by dots:
<base64url(header)>.<base64url(payload)>.<base64url(signature)>
1. Header
The JWT header specifies the signing algorithm:
{
"alg": "ETH",
"typ": "JWT"
}
Base64url-encode this JSON to create the header part.
2. Payload
The JWT payload contains request metadata and a digest of the request body:
{
"digest": "0x<sha256_hash_of_request_body>",
"iss": "0xYourEVMAddress",
"iat": 1762807282,
"exp": 1762807582,
"jti": "550e8400-e29b-41d4-a716-446655440000"
}
Payload fields:
| Field | Description |
|---|---|
digest | SHA256 hash of the JSON-RPC request body (with 0x prefix) |
iss | Issuer - your EVM address (the public key corresponding to your private key) |
iat | Issued at time (Unix timestamp in seconds) |
exp | Expiration time (Unix timestamp, max 5 minutes after iat) |
jti | JWT ID (UUID v4 for replay protection) |
Computing the digest
The digest is a SHA256 hash of your JSON-RPC request body serialized as UTF-8 encoded JSON in ascending lexicographic order (sorted by key names):
Original request:
{
"jsonrpc": "2.0",
"id": "req-123",
"method": "workflows.execute",
"params": {
"input": { "key1": "value1", "key2": "value2" },
"workflow": { "workflowID": "a1b2c3..." }
}
}
Keys must be sorted alphabetically at every level:
{
"id": "req-123",
"jsonrpc": "2.0",
"method": "workflows.execute",
"params": { "input": { "key1": "value1", "key2": "value2" }, "workflow": { "workflowID": "a1b2c3..." } }
}
Then compute: digest = "0x" + sha256(sorted_json_string)
3. Signature
The signature is an ECDSA signature of the message <base64url(header)>.<base64url(payload)> using your private key.
Signing process:
- Concatenate the encoded header and payload:
message = base64url(header) + "." + base64url(payload) - Sign the message using ECDSA with your private key:
- Prepend the Ethereum signed message prefix:
"\x19Ethereum Signed Message:\n" + len(message) + message - Hash the prefixed message with Keccak256
- Sign the hash using your private key
- Prepend the Ethereum signed message prefix:
- Extract the signature components:
r(32 bytes),s(32 bytes),v(1 byte, recovery ID) - Concatenate:
signature_bytes = r || s || v - Base64url-encode the signature bytes
Reference implementations
Manual JWT generation requires careful cryptographic operations. Use these reference implementations as guidance:
For Go
Production-grade utilities:
- JWT creation: jwt.go - See the
CreateRequestJWTmethod for complete JWT generation - ECDSA signatures: eth_signatures.go - See the
GenerateEthSignaturemethod for Ethereum-compatible signing
For TypeScript/JavaScript
The CRE SDK includes a reference implementation using viem:
- Complete implementation: cre-http-trigger source code
- Key files:
create-jwt.ts- JWT header, payload, and signing logicutils.ts- SHA256 hashing and base64url encoding helpers
For testing
If you're testing deployed workflows during development, use the Local Testing Tool which runs a local proxy server that handles the entire JWT generation and request flow automatically.
Example request (conceptual)
Here's what a complete curl request looks like:
curl -X POST https://01.gateway.zone-a.cre.chain.link \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJFVEgiLCJ0eXAiOiJKV1QifQ.eyJkaWdlc3QiOiIweDRhMWYyYjNjNGQ1ZTZmN2E4YjljMGQxZTJmM2E0YjVjNmQ3ZThmOWEwYjFjMmQzZTRmNWE2YjdjOGQ5ZTBmMWEiLCJpc3MiOiIweGIwOEUwMDRiZDJiNWFGZjFGNUY5NTBkMTQxZjQ0OUIxYzA1ODAwZWIiLCJpYXQiOjE3MzM4MzIwMDAsImV4cCI6MTczMzgzMjMwMCwianRpIjoiNTUwZTg0MDAtZTI5Yi00MWQ0LWE3MTYtNDQ2NjU1NDQwMDAwIn0.r7s8v9recoveryId..." \
-d '{
"jsonrpc": "2.0",
"id": "req-123",
"method": "workflows.execute",
"params": {
"input": {
"userId": "user_123",
"action": "purchase",
"amount": 100
},
"workflow": {
"workflowID": "a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890"
}
}
}'
Response format
Success response
When your request is successfully accepted, the gateway returns a JSON-RPC response:
{
"jsonrpc": "2.0",
"id": "req-123",
"method": "workflows.execute",
"result": {
"workflow_id": "<your-workflow-id>",
"workflow_execution_id": "<your-workflow-execution-id>",
"status": "ACCEPTED"
}
}
Response fields:
| Field | Description |
|---|---|
jsonrpc | JSON-RPC version (always "2.0") |
id | The request ID you provided in the request |
method | The method called (always "workflows.execute") |
result.workflow_id | Your workflow ID (with 0x prefix) |
result.workflow_execution_id | Unique execution ID for this workflow run (use this to track execution in the CRE UI) |
result.status | Execution status (typically "ACCEPTED" when the workflow trigger is successfully accepted by the gateway) |
Error response
If the request fails (e.g., invalid JWT, unauthorized key, workflow not found), the gateway returns an error response. Example:
{
"jsonrpc": "2.0",
"id": "req-123",
"method": "",
"error": {
"code": -32600,
"message": "Auth failure: signer '0x...' is not authorized for workflow '0x...'. Ensure that the signer is registered in the workflow definition"
}
}
Verifying execution
After triggering your workflow, verify execution in the CRE UI:
- Go to cre.chain.link/workflows
- Click on your workflow
- Check the Execution tab for recent runs
- Click on an Execution ID to view detailed logs and events
See Monitoring & Debugging Workflows for complete monitoring guidance.
Security considerations
Private key protection
- Never commit private keys to version control
- Use environment variables or secret management tools (e.g., AWS Secrets Manager, HashiCorp Vault)
- Rotate keys periodically and update your workflow's
authorizedKeysif compromised
Request expiration
- JWT tokens expire after the
exptimestamp (max 5 minutes afteriat) - This prevents replay attacks with captured requests
- Generate new JWTs for each request
Replay protection
- The
jti(JWT ID) field provides replay protection - Use a unique UUID for every request
- The gateway may reject duplicate
jtivalues within the expiration window
Next steps
For easier testing
Manual JWT generation is complex and error-prone. For development and testing:
- Local Testing Tool - Automatically generates JWTs and sends requests
Additional resources
- HTTP Trigger SDK Reference - Complete API documentation
- Monitoring Workflows - Track execution history and debug issues