--- title: Tasks description: "Tasks are the main way that developers will interact with your Integration. They are the actions that developers will be able to perform in their jobs." --- ## runTask You should add a `runTask()` function to your integration, before adding individual Tasks. It will be used by all Tasks, and allows users to use any function in the official SDK as a Task. ```ts integrations/github/index.ts export class Github implements TriggerIntegration { ... runTask | void>( key: IntegrationTaskKey, //change Octokit to the official SDK for your integration callback: (client: Octokit, task: IOTask, io: IO) => Promise, options?: RunTaskOptions, errorCallback?: RunTaskErrorCallback ): Promise { if (!this._io) throw new Error("No IO"); if (!this._connectionKey) throw new Error("No connection key"); return this._io.runTask( key, (task, io) => { if (!this._client) throw new Error("No client"); return callback(this._client, task, io); }, { //change this to the slug for your integration (lowercase, no spaces) icon: "github", retry: retry.standardBackoff, ...(options ?? {}), connectionKey: this._connectionKey, }, errorCallback ); } ... } ``` ```ts Usage //Users can then use it in their Jobs like this: client.defineJob({ ... integrations: { github, }, run: async (payload, io, ctx) => { //io.github.runTask allows you to use the underlying SDK client const { data } = await io.github.runTask( "create-card", async (client) => { //this is octokit, authenticated for the user return client.rest.projects.createCard({ column_id: 123, note: "test", }); }, //this is optional, displays in the dashboard when viewing a Run { name: "Create card" } ); }, }); ``` ## Matching the original SDK structure The structure of the integrtion should closely map to the official Node SDK for that API. | API | Official SDK | Trigger.Dev Task | | ---------------- | --------------------------- | ------------------------------ | | GitHub (octokit) | `client.rest.issues.create` | `io.github.issues.create` | | Typeform | `client.forms.get` | `io.typeform.forms.get` | | OpenAI | `client.completions.create` | `io.openai.completions.create` | Closely resembling the official SDK makes it far easier for developers to use integrations. Aim for 1:1 mapping wherever possible. ## Adding Tasks Any tasks you add will use the `runTask` function you added above. Remember we want to structure our integration to match the original SDK. If an SDK has a `client.forms.get` function, we want to add a `myintegration.forms.get` task. This requires adding a bit of structure to the SDK, to add the Forms object, the get function and use `runTask` inside them. ### Create the structure In this example, we'll add `io.github.issues.create`. This will allow users to create a GitHub issue in their jobs. Comments inline are for instructional purposes only, and should not be included in your final code. ```ts integration/github/issues.ts import { IntegrationTaskKey } from "@trigger.dev/sdk"; import { GitHubReturnType, GitHubRunTask, onError } from "./index"; import { Octokit } from "octokit"; export class Issues { //the runTask you created in the other file, we'll use this for all Tasks runTask: GitHubRunTask; constructor(runTask: GitHubRunTask) { this.runTask = runTask; } create( //this must be the first parameter, it's used to identify the task key: IntegrationTaskKey, //if possible use the official SDK type. Here we had to define our own. params: { title: string; owner: string; repo: string } //you must define the return type (it will be a promise, in this case grabbed from the official SDK) ): GitHubReturnType { //use runTask return this.runTask( key, async (client, task) => { //the official SDK is used here const result = await client.rest.issues.create({ owner: params.owner, repo: params.repo, title: params.title, }); return result.data; }, //all of these properties are displayed in the Trigger.dev dashboard { name: "Create Issue", params, //properties provide a great experience for developers debugging a Run properties: [ { label: "Owner", text: params.owner, }, { label: "Repo", text: params.repo, }, { label: "Title", text: params.title, }, ], }, //you can define a custom error function, or omit this. onError ); } } ``` Notice how the params are the _second_ argument, that's because the first argument is always the task key. See our [Keys and Resumability docs](/documentation/concepts/resumability) for more on why this is important. ```ts integrations/github/index.ts //This type is used in the Issues class above export type GitHubRunTask = InstanceType["runTask"]; export class Github implements TriggerIntegration { ... //this allows us to do `io.github.issues.create()` get issues() { //the bind is needed to preserve the `this` context return new Issues(this.runTask.bind(this)); } ... } ``` ### `onError` function You can optionally have logic in the `onError` param. See [the reference for runTask](/sdk/io/runtask) for more info. The GitHub integration uses this to retry rate-limited requests when the rate limit resets: ```ts integrations/github/index.ts ... function isRequestError(error: unknown): error is RequestError { return typeof error === "object" && error !== null && "status" in error; } export function onError(error: unknown) { if (!isRequestError(error)) { return; } // Check if this is a rate limit error if (error.status === 403 && error.response) { const rateLimitRemaining = error.response.headers["x-ratelimit-remaining"]; const rateLimitReset = error.response.headers["x-ratelimit-reset"]; if (rateLimitRemaining === "0" && rateLimitReset) { const resetDate = new Date(Number(rateLimitReset) * 1000); return { retryAt: resetDate, error, }; } } } ```