Testing with Local JWT Server

The cre-http-trigger TypeScript package simplifies testing deployed workflows with HTTP triggers by handling JWT generation, signing, and request formatting automatically. It provides a local HTTP server that acts as a proxy between your test requests and the CRE gateway.

Manually creating JWT tokens for HTTP trigger requests involves complex steps: computing SHA256 digests with sorted JSON keys, generating ECDSA signatures, and encoding everything correctly (see Triggering Deployed Workflows).

The cre-http-trigger tool eliminates this complexity by:

  1. Managing JWT generation - Automatically creates properly signed JWT tokens
  2. Handling request formatting - Constructs valid JSON-RPC requests
  3. Providing a simple API - Send test requests with plain JSON payloads
  4. Enabling local testing - Run a local server that forwards requests to the gateway

Prerequisites

  • Bun runtime: The tool requires Bun version 1.2.21 or higher
  • Deployed workflow: Your workflow must be deployed with an HTTP trigger
  • Workflow ID: Available from deployment output or the CRE UI
  • Private key: The private key corresponding to one of the authorizedKeys in your HTTP trigger configuration

Installation

Clone the CRE SDK TypeScript repository and navigate to the HTTP trigger package:

git clone https://github.com/smartcontractkit/cre-sdk-typescript.git
cd cre-sdk-typescript/packages/cre-http-trigger

Setup

1. Install dependencies

bun install

2. Configure environment variables

Create a .env file in the package root:

PRIVATE_KEY=0xYourPrivateKeyHere
GATEWAY_URL=https://01.gateway.zone-a.cre.chain.link

Replace 0xYourPrivateKeyHere with your EVM private key.

3. Start the server

Run the local proxy server:

bun start

The server starts on port 2000 by default. You should see:

🚀 HTTP Trigger Server running at http://localhost:2000

Usage

With the server running, you can trigger workflows by sending HTTP POST requests to http://localhost:2000/trigger.

Basic request

Send a POST request with your workflow ID as a query parameter and input data in the body:

curl -X POST "http://localhost:2000/trigger?workflowID=<your-workflow-id>" \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "userId": "user_123",
      "action": "purchase",
      "amount": 100
    }
  }'

Request format

Query parameter:

FieldTypeDescription
workflowIDstringYour 64-character workflow ID (no 0x prefix)

Request body:

FieldTypeDescription
inputobjectJSON payload passed to your workflow callback

Example

curl -X POST "http://localhost:2000/trigger?workflowID=<your-workflow-id>" \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "userId": "user_456",
      "action": "purchase",
      "amount": 250
    }
  }'

Server logs:

When the server processes your request, you'll see logs in the terminal where the server is running:

🚀 Triggering workflow...

   Workflow: {
  workflowID: "<your-workflow-id>",
}

   Input: {
  "userId": "user_456",
  "action": "purchase",
  "amount": 250
}

   Signed by: <your-public-address>

   Response: Response (297 bytes) {
  ok: true,
  url: "https://01.gateway.zone-a.cre.chain.link/",
  status: 200,
  statusText: "OK",
  ...
}

Client response:

Your curl command receives a JSON response:

{
  "success": true,
  "response": {
    "jsonrpc": "2.0",
    "id": "<your-request-id>",
    "method": "workflows.execute",
    "result": {
      "workflow_id": "<your-workflow-id>",
      "workflow_execution_id": "<your-workflow-execution-id>",
      "status": "ACCEPTED"
    }
  }
}

Response fields:

Field
Description
successAlways true for successful proxy requests
response.jsonrpcJSON-RPC version (always "2.0")
response.idUnique request ID generated by the tool
response.methodAlways "workflows.execute"
result.workflow_idYour workflow ID (with 0x prefix)
result.workflow_execution_idUnique execution ID for this workflow run (use this to track execution in the CRE UI)
result.statusExecution status (typically "ACCEPTED" for valid requests)

Health check endpoint

Verify the server is running:

curl http://localhost:2000/health

Response:

OK

How it works

The tool performs the following steps automatically:

  1. Receives your request at /trigger with workflowID as a query parameter and input in the request body
  2. Loads configuration from environment variables (PRIVATE_KEY, GATEWAY_URL)
  3. Constructs JSON-RPC payload:
    {
      "jsonrpc": "2.0",
      "id": "generated-uuid",
      "method": "workflows.execute",
      "params": {
        "input": { ... },
        "workflow": { "workflowID": "..." }
      }
    }
    
  4. Computes digest of the request body (SHA256 with sorted keys)
  5. Creates JWT payload with digest, iss, iat, exp, jti
  6. Signs JWT using your private key with ECDSA
  7. Sends authenticated request to the CRE gateway with the JWT in the Authorization header
  8. Returns the response to your test client

This eliminates the need to manually handle cryptographic operations and request formatting.

Code structure

If you're curious about the implementation or need to customize the tool:

FilePurpose
src/index.tsHTTP server setup (Bun server with /health and /trigger endpoints)
src/trigger-workflow.tsMain orchestration: calls config, JWT generation, and sends request
src/create-jwt.tsJWT creation: header, payload, signing with ECDSA
src/get-config.tsLoads PRIVATE_KEY and GATEWAY_URL from environment
src/schemas.tsZod schemas for input validation
src/utils.tsHelper functions (SHA256 hashing, base64url encoding)

All code is available in the cre-http-trigger package.

Monitoring execution

After triggering workflows with the tool, verify execution in the CRE UI:

  1. Go to cre.chain.link/workflows
  2. Click on your workflow
  3. Navigate to the Execution tab
  4. Find recent executions and click Execution ID to view Events and Logs

See Monitoring & Debugging Workflows for complete guidance.

Troubleshooting

"Invalid private key" error

Cause: The PRIVATE_KEY environment variable is missing or malformed.

Solution: Ensure your .env file contains a valid EVM private key (with or without 0x prefix).

"Unauthorized" error

Cause: The private key doesn't correspond to any of the authorizedKeys in your workflow's HTTP trigger configuration.

Solution: Verify that the public address derived from your private key matches an entry in your workflow's authorizedKeys.

"Workflow not found" error

Cause: Incorrect workflowID or the workflow isn't deployed.

Solution: Double-check the workflow ID from the CRE UI or deployment output.

Server won't start

Cause: Port 2000 is already in use.

Solution: Stop any other services using port 2000, or modify src/index.ts to use a different port.

Production considerations

This tool is not intended for production use. For production integrations:

  1. Implement JWT generation in your backend - Use your backend service's language and crypto libraries
  2. Secure private key storage - Use a secrets manager
  3. Add retry logic - Handle transient network errors and gateway timeouts
  4. Monitor and log - Track requests, responses, and execution outcomes
  5. Rate limiting - Implement appropriate request throttling

Next steps

Get the latest Chainlink content straight to your inbox.