drizzle
Use this skill when writing or modifying Drizzle ORM schemas, queries, or migrations in this repo — specifically the…
Integrates Trigger.dev background tasks with Convex using @trigger.dev/convex. Use when triggering work transactionally from a Convex mutation, showing reactive run state, handling completion callbacks, combining Trigger Realtime with Convex, restoring jobs after reload, or
Claude Code
$ npx -y skills add triggerdotdev/trigger.dev --skill trigger-convex --agent claude-code
Codex
$ npx skills add triggerdotdev/trigger.dev --skill trigger-convex -a codex -y
Cursor
$ npx skills add triggerdotdev/trigger.dev --skill trigger-convex -a cursor -y
Gemini CLI
$ npx skills add triggerdotdev/trigger.dev --skill trigger-convex -a gemini-cli -y
GitHub Copilot
$ npx skills add triggerdotdev/trigger.dev --skill trigger-convex -a github-copilot -y
OpenCode
$ npx skills add triggerdotdev/trigger.dev --skill trigger-convex -a opencode -y
Windsurf
$ npx skills add triggerdotdev/trigger.dev --skill trigger-convex -a windsurf -y
name: trigger-convex description: > Integrates Trigger.dev background tasks with Convex using @trigger.dev/convex. Use when triggering work transactionally from a Convex mutation, showing reactive run state, handling completion callbacks, combining Trigger Realtime with Convex, restoring jobs after reload, or repairing paused synchronization. Covers ownership, idempotency, scoped browser tokens, and credential-safe task payloads. Not for Convex-only applications or Trigger.dev applications without Convex. license: Apache-2.0
Locate `@trigger.dev/convex/package.json`, then read the adjacent `README.md` and `dist/client/index.d.ts`. In a non-hoisted installation, resolve the package with:
node -p "require.resolve('@trigger.dev/convex/package.json')"Use the installed declarations as authority. Older releases do not expose `refresh()`. If it is absent, use durable polling or upgrade to a compatible release; do not invent a replacement method or accept browser-supplied run state. Keep the application's Convex version compatible with the package's peer dependency. Do not pin an arbitrary preview URL unless the user explicitly needs that preview.
Choose the relevant flow:
| Request | Approach | | --- | --- | | Enqueue a report, render, import, or AI task | Call `trigger.trigger` inside the application's authenticated mutation. | | Display durable status/results | Authorize the application's job, then call `trigger.getRun` from a query. | | Update application records on completion | Register a short internal mutation using `vRun`. | | Display responsive browser progress | Subscribe to Trigger Realtime; request an authorized Convex refresh on terminal events. | | Restore an edit after reload or leaving the page | Persist the selected application job/request ID and inputs; recover owned work from Convex. | | Recover paused status synchronization | Repair credentials/callback failures, then call authorized `resume`; do not enqueue another task. |
Install `@trigger.dev/convex` and `convex`. Deploy the actual task separately with Trigger.dev; the component does not deploy task code. Keep `TRIGGER_SECRET_KEY` only in the Convex server environment, for the intended Trigger environment.
// convex/convex.config.ts
import { defineApp } from "convex/server";
import triggerComponent from "@trigger.dev/convex/convex.config.js";
const app = defineApp();
app.use(triggerComponent);
export default app;Run `npx convex dev` to generate the application's component references.
// convex/trigger.ts
import { Trigger } from "@trigger.dev/convex";
import { components } from "./_generated/api";
export const trigger: Trigger = new Trigger(components.trigger);Constructor options are `secretKey`, `baseUrl`, and `onComplete`. The default API is `https://api.trigger.dev`; `TRIGGER_API_URL` is not automatically read by the component. Set `baseUrl` explicitly for a different API host. HTTP is allowed only for localhost development. Keep server keys out of `VITE_`/`NEXT_PUBLIC_` variables.
Maintain an application-owned job table. The component has no user/tenant model. This example assumes an existing deployed `process-text` task that accepts `{ text }`. Validate business limits and authorize any referenced documents/files before enqueueing.
// convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
jobs: defineTable({
owner: v.string(),
requestId: v.string(),
text: v.string(),
componentRunId: v.optional(v.string()),
}).index("by_owner_request", ["owner", "requestId"])
.index("by_componentRunId", ["componentRunId"]),
});// convex/jobs.ts
import { vRun } from "@trigger.dev/convex";
import { v } from "convex/values";
import { mutation, query } from "./_generated/server";
import { trigger } from "./trigger";
export const request = mutation({
args: { text: v.string(), requestId: v.string() },
returns: v.id("jobs"),
handler: async (ctx, args) => {
const user = await ctx.auth.getUserIdentity();
if (!user) throw new Error("Unauthorized");
const owner = user.tokenIdentifier;
const existing = await ctx.db.query("jobs")
.withIndex("by_owner_request", q => q.eq("owner", owner).eq("requestId", args.requestId))
.unique();
if (existing) {
if (existing.text !== args.text) throw new Error("Request ID reused for different input");
return existing._id;
}
const jobId = await ctx.db.insert("jobs", { owner, ...args });
const componentRunId = await trigger.trigger(ctx, "process-text", { text: args.text }, {
idempotencyKey: `process-text:${owner}:${args.requestId}`,
});
await ctx.db.patch(jobId, { componentRunId });
return jobId;
},
});
export const status = query({
args: { jobId: v.id("jobs") },
returns: v.union(vRun, v.null()),
handler: async (ctx, { jobId }) => {
const user = await ctx.auth.getUserIdentity();
const job = await ctx.db.get(jobId);
if (!user || !job || job.owner !== user.tokenIdentifier) return null;
return job.componentRunId ? trigger.getRun(ctx, job.componentRunId) : null;
},
});Preserve the same request ID when retrying an unconfirmed submission. Namespace component idempotency keys by tenant/owner, task, and logical request. They dedupe across the entire component instance, not just a task, and do not compare payloads. An application mutation rollback also rolls back the component enqueue/scheduling. Do not replace this with a direct SDK trigger from an action if atomicity is required. Use type-only task imports when adding task payload inference; do not bundle task implementations, Sharp, or provider clients into the Convex mutation runtime.
There are three di
The quickest way to get started is to create an account and project in our web app, and follow the instructions in the onboarding. Build and deploy your first task in minutes.
Use this skill when writing or modifying Drizzle ORM schemas, queries, or migrations in this repo — specifically the…
End-to-end smoke test for the public Errors HTTP API (error groups). Seeds failed runs into ClickHouse so the error materialized…
Use when adding, modifying, or debugging OTel span timeline events in the trace view. Covers event structure, ClickHouse storage…
Use this skill when writing, designing, or optimizing Trigger.dev background tasks and workflows. This includes creating reliable…
Author and run a durable AI chat agent with chat.agent from @trigger.dev/sdk/ai: the per-turn run loop, why you MUST take…
Covers writing backend Trigger.dev tasks with @trigger.dev/sdk: defining task() and schemaTask(), the run function and its ctx,…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.