Backend

Actions

Run arbitrary code including external API calls with Convex actions.

Actions run arbitrary code including external API calls. Unlike queries and mutations, actions cannot directly access the database.

Basic Action

Create an action that calls an external API:

convex/ai.ts
import { v } from 'convex/values'
import { action } from './_generated/server'

export const summarize = action({
  args: { text: v.string() },
  handler: async (ctx, { text }) => {
    const response = await fetch('https://api.openai.com/v1/...', {
      method: 'POST',
      headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` },
      body: JSON.stringify({ prompt: text }),
    })
    return await response.json()
  },
})

When to Use Actions

Use actions when you need to:

  • Call external APIs (payment providers, AI services, etc.)
  • Perform long-running operations
  • Use libraries that require Node.js APIs
  • Execute side effects that shouldn't be transactional
Actions cannot read from or write to the database directly. Use ctx.runQuery() and ctx.runMutation() to interact with data.

Calling Queries and Mutations

Actions can call other Convex functions:

convex/tasks.ts
import { v } from 'convex/values'
import { api } from './_generated/api'
import { action } from './_generated/server'

export const processAndSave = action({
  args: { text: v.string(), userId: v.string() },
  handler: async (ctx, { text, userId }) => {
    // Call external API
    const processed = await processText(text)

    // Save result using a mutation
    await ctx.runMutation(api.tasks.add, {
      title: processed,
      userId,
    })

    return processed
  },
})

Using in Components

Call actions from Vue components with useConvexAction:

app/components/Summarize.vue
<script setup lang="ts">
import { api } from '#convex/api'

const { execute: summarize, isLoading, error } = useConvexAction(api.ai.summarize)
const result = ref('')

async function handleSummarize(text: string) {
  result.value = await summarize({ text })
}
</script>

<template>
  <div>
    <button :disabled="isLoading" @click="handleSummarize('Some text')">
      Summarize
    </button>
    <p v-if="result">
      {{ result }}
    </p>
    <p v-if="error">
      {{ error.message }}
    </p>
  </div>
</template>

See useConvexAction for full documentation.

Environment Variables

Access environment variables in actions through process.env:

convex/integrations.ts
export const sendEmail = action({
  args: { to: v.string(), subject: v.string(), body: v.string() },
  handler: async (ctx, { to, subject, body }) => {
    await fetch('https://api.sendgrid.com/v3/mail/send', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.SENDGRID_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ to, subject, body }),
    })
  },
})

Set environment variables in the Convex dashboard under Settings → Environment Variables.

Next Steps