Skip to main content
Your compose tasks can be triggered by several different mechanisms, triggers are configured in your manifest file for each task. Each task can have multiple trigger types, thus you configure tasks with an array of triggers. Below is an overview of all the supported trigger types.

Local Task Execution

For locally testing tasks you can use the callTask CLI method, this can be done locally regardless of what types of triggers you have configured.

Trigger Task locally with CLI

goldsky compose callTask "my-task" '{ "foo": "bar" }'

Task Code

import { TaskContext } from "compose";

type Payload = {
  foo: string;
};

export async function main({ collection }: TaskContext, payload: Payload) {
  // the task will be called with the payload sent from the CLI callTask command
  console.log(payload.foo);
}

Task To Task Execution

Tasks can execute other tasks by default. This is done by using the callTask context function.

Example

import { TaskContext } from "compose";

type ProcessDataPayload = {
  dataId: string;
  operation: string;
};

export async function main({ callTask }: TaskContext, payload: ProcessDataPayload) {
  // Call another task with a payload
  const result = await callTask<{ success: boolean; processed: number }>(
    "process-data",
    {
      dataId: payload.dataId,
      operation: payload.operation,
    }
  );

  return {
    status: "completed",
    result,
  };
}
The process-data task will receive the payload and can return a response:
import { TaskContext } from "compose";

type ProcessDataArgs = {
  dataId: string;
  operation: string;
};

export async function main({ collection }: TaskContext, payload: ProcessDataArgs) {
  // Process the data based on the operation
  const processed = await collection("data").findMany({ id: payload.dataId });

  return {
    success: true,
    processed: processed.length,
  };
}

Chain Event Triggers

Tasks can be triggered with onchain events, the payload sent to the task will be the encoded event object. Compose provides useful tools for decoding the event in your task code, see Contracts for more details on decoding.

Example event object sent as the payload to your task:

{
  "blockNumber": 48758053,
  "blockHash":
    "0x6794a56583329794f184d50862019ecf7b6d8ba6b3210f68ca4b91a8fa81817d",
  "transactionIndex": 29,
  "removed": false,
  "address": "0xb74de3F91e04d0920ff26Ac28956272E8d67404D",
  "data": "0x",
  "topics": [
    "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    "0x0000000000000000000000000000000000000000000000000000000000000000",
    "0x000000000000000000000000eec2ba9b9f0202c63bba29ea9a4ce5c23f9865fd",
    "0x0000000000000000000000000000000000000000000000000000000000001099",
  ],
  "transactionHash":
    "0x2c500a55f5c24d587e73805975d91395634a971dca5939f43d34d774d0f7147b",
  "logIndex": 343,
}

Example configuration for an onchain event trigger:

name: "my-app"
tasks:
  - name: "hourly_sync"
    path: "./tasks/sync.ts"
    triggers: 
      - type: "onchain"
        network: "base-sepolia"
        contract: "0xb74de3F91e04d0920ff26Ac28956272E8d67404D"
        events:
          - "Transfer(address,address,uint256)"

Task code

Here’s an example in which we decode the event with a contract class we’ve generated with goldsky compose codegen, see Contracts for more info.
import { TaskContext, OnchainEvent } from "compose";

// the payload for an onchain triggered task will always be our built in OnchainEvent interface
export async function main({ context }: TaskContext, payload: OnchainEvent) {
  // we can decode the event with our ABI generated contract class
  // the type of this decodedEvent will be a union of all events that the ABI specifies, allowing you to conditionally process based on the eventName
  const decodedEvent = context.evm.contracts.MyNFT.decodeEventLog(payload);

  if (decodedEvent.eventName === "Transfer") {
    // in this code block typescript will know the decodedEvent is of type TransferEventDecoded
    // this pattern is useful when you trigger supports multiple event types
    console.log("Transfer event:", decodedEvent.args);
  }

  // alternative would be to cast, this is useful if your trigger is configured for a single event type
  const decodedEvent = context.evm.contracts.MyNFT.decodeEventLog<TransferEventDecoded>(payload);
}

Onchain Trigger Properties

PropertyTypeRequiredDescription
type”onchain”YesType discriminator
networkstringYesThe slug value of the network
contractstringYesThe address of the contract
eventsarray(string)NoSpecific event signatures (not encoded) to use for the trigger

Cron Triggers

Tasks can be triggered by cron jobs, tasks triggered by cron will be called with an empty payload since each invocation is generic.

Example

name: "my-app"
tasks:
  - name: "hourly_sync"
    path: "./tasks/sync.ts"
    triggers: 
      - type: "cron"
        expression: "0 * * * *" # Every hour at minute 0

Cron Trigger Properties

PropertyTypeRequiredDescription
type”cron”YesType discriminator
expressionstringYesThe interval to call the task in cron expression syntax

HTTP Triggers

Tasks can be triggered by http requests, often this is useful for starting compose tasks from your application logic. HTTP triggers can be sent any payload you want for dynamic execution.

Example

name: "my-app"
tasks:
  - name: "status"
    path: "./tasks/status.ts"
    triggers:
      - type: "http"
        authentication: "auth_token" # other option is "none"

Http Trigger Properties

PropertyTypeRequiredDescription
type”http”YesType discriminator
authentication”auth_token” | “none”YesType of authentication to use on the endpoint
role”Owner” | “Admin” | “Editor” | “Viewer”NoThe minimum RBAC role required if using “auth_token” authentication
Authenticated triggers require a goldsky auth token when the app is deployed, but can be triggered without auth when testing locally. Unauthenticated triggers can be called without auth locally and when deployed.

Base URL

Locally, Compose runs on http://localhost:4000. The base pattern for all endpoints is:
http://localhost:4000/tasks/{endpoint}
When deployed to the cloud, all tasks configured for authenticated http triggering can be found at this url pattern:
https://api.goldsky.com/api/admin/compose/v1/{appName}/tasks/{taskName}
When deployed to the cloud, all tasks configured for unauthenticated http triggering can be found at this url pattern:
https://api.goldsky.com/api/public/compose/v1/{projectId}/{appName}/tasks/{taskName}

HTTP Request Format

curl -X POST http://localhost:4000/tasks/{taskName} \
  -H "Content-Type: application/json" \
  -d '{"key": "value"}'
If you are running in the cloud with http trigger type of “authenticated”, you’ll need to pass a goldsky API token like so:
curl -X POST https://api.goldsky.com/api/admin/compose/v1/{appName}/tasks/{taskName}
  -H "Authorization: Bearer {token}" -H "Content-Type: application/json" 
  -d '{"foo": "foo"}' 

Delayed Task Execution

You can schedule a task to execute after a delay by including a delay field in your request body. The delay is specified in milliseconds.
curl -X POST http://localhost:4000/tasks/{taskName} \
  -H "Content-Type: application/json" \
  -d '{
    "delay": 5000,
    "key": "value"
  }'
This will execute the task after 5000 milliseconds (5 seconds). The task receives all parameters except delay in its payload.

Execute HTTP trigger without parameters

curl -X POST http://localhost:4000/tasks/bitcoin-oracle \
  -H "Content-Type: application/json" \
  -d '{}'

Execute an HTTP trigger with parameters

curl -X POST http://localhost:4000/tasks/bitcoin-oracle \
  -H "Content-Type: application/json" \
  -d '{
    "contractAddress": "0x742d35Cc6634C0532925a3b8D6Ac6E7D9C3c1234",
    "threshold": 1000
  }'

Response Format

Tasks return JSON responses with the data returned by the task’s main function. Calling this task:
import { TaskContext } from "compose";

export async function main({ logEvent }: TaskContext, payload: { name?: string }) {
  const name = payload?.name || "World";

  return { success: true, greeting: `Hello, ${name}!` };
}
via this request:
curl -X POST http://localhost:4000/tasks/greeting \
  -H "Content-Type: application/json" \
  -d '{
    "name":"Adam"
  }'
returns this JSON response:
{
  "success": true,
  "greeting": "Hello, Adam!"
}

Next Steps