Skills Plugins MCP Prompt Model 博客 我的中心
Development #api #web

convex-http-actions

External API integration and webhook handling including HTTP endpoint routing, request/response handling, authentication, CORS configuration, and webhook signature validation

DeepseekModel Curated skill Quality Excellent · 78 v1.0.0

Get

https://deepseekmodel.com/api/download.php?id=waynesutton-convexskills-skills-convex-http-actions-skill-md&format=skill
Download .skill Standard format with system_prompt and model_config, ready for any agent framework
The actual content of the system_prompt field in the .skill file.
name convex-http-actions displayName Convex HTTP Actions description External API integration and webhook handling including HTTP endpoint routing, request/response handling, authentication, CORS configuration, and webhook signature validation version 1.0.0 author Convex tags ["convex","http","actions","webhooks","api","endpoints"] Convex HTTP Actions Build HTTP endpoints for webhooks, external API integrations, and custom routes in Convex applications. Documentation Sources Before implementing, do not assume; fetch the latest documentation: Primary: https://docs.convex.dev/functions/http-actions Actions Overview: https://docs.convex.dev/functions/actions Authentication: https://docs.convex.dev/auth For broader context: https://docs.convex.dev/llms.txt Instructions HTTP Actions Overview HTTP actions allow you to define HTTP endpoints in Convex that can: Receive webhooks from third-party services Create custom API routes Handle file uploads Integrate with external services Serve dynamic content Basic HTTP Router Setup // convex/http.ts import { httpRouter } from "convex/server" ; import { httpAction } from "./_generated/server" ; const http = httpRouter (); // Simple GET endpoint http. route ({ path : "/health" , method : "GET" , handler : httpAction ( async (ctx, request) => { return new Response ( JSON . stringify ({ status : "ok" }), { status : 200 , headers : { "Content-Type" : "application/json" }, }); }), }); export default http; Request Handling // convex/http.ts import { httpRouter } from "convex/server" ; import { httpAction } from "./_generated/server" ; const http = httpRouter (); // Handle JSON body http. route ({ path : "/api/data" , method : "POST" , handler : httpAction ( async (ctx, request) => { // Parse JSON body const body = await request. json (); // Access headers const authHeader = request. headers . get ( "Authorization" ); // Access URL parameters const url = new URL (request. url ); const queryParam = url. searchParams . get ( "filter" ); return new Response ( JSON . stringify ({ received : body, filter : queryParam }), { status : 200 , headers : { "Content-Type" : "application/json" }, } ); }), }); // Handle form data http. route ({ path : "/api/form" , method : "POST" , handler : httpAction ( async (ctx, request) => { const formData = await request. formData (); const name = formData. get ( "name" ); const email = formData. get ( "email" ); return new Response ( JSON . stringify ({ name, email }), { status : 200 , headers : { "Content-Type" : "application/json" }, } ); }), }); // Handle raw bytes http. route ({ path : "/api/upload" , method : "POST" , handler : httpAction ( async (ctx, request) => { const bytes = await request. bytes (); const contentType = request. headers . get ( "Content-Type" ) ?? "application/octet-stream" ; // Store in Convex storage const blob = new Blob ([bytes], { type : contentType }); const storageId = await ctx. storage . store (blob); return new Response ( JSON . stringify ({ storageId }), { status : 200 , headers : { "Content-Type" : "application/json" }, } ); }), }); export default http; Path Parameters Use path prefix matching for dynamic routes: // convex/http.ts import { httpRouter } from "convex/server" ; import { httpAction } from "./_generated/server" ; const http = httpRouter (); // Match /api/users/* with pathPrefix http. route ({ pathPrefix : "/api/users/" , method : "GET" , handler : httpAction ( async (ctx, request) => { const url = new URL (request. url ); // Extract user ID from path: /api/users/123 -> "123" const userId = url. pathname . replace ( "/api/users/" , "" ); return new Response ( JSON . stringify ({ userId }), { status : 200 , headers : { "Content-Type" : "application/json" }, } ); }), }); export default http; CORS Configuration // convex/http.ts import { httpRouter } from "convex/server" ; import { httpAction } from "./_generated/server" ; const http = httpRouter (); // CORS headers helper const corsHeaders = { "Access-Control-Allow-Origin" : "*" , "Access-Control-Allow-Methods" : "GET, POST, PUT, DELETE, OPTIONS" , "Access-Control-Allow-Headers" : "Content-Type, Authorization" , "Access-Control-Max-Age" : "86400" , }; // Handle preflight requests http. route ({ path : "/api/data" , method : "OPTIONS" , handler : httpAction ( async () => { return new Response ( null , { status : 204 , headers : corsHeaders, }); }), }); // Actual endpoint with CORS http. route ({ path : "/api/data" , method : "POST" , handler : httpAction ( async (ctx, request) => { const body = await request. json (); return new Response ( JSON . stringify ({ success : true , data : body }), { status : 200 , headers : { "Content-Type" : "application/json" , ...corsHeaders, }, } ); }), }); export default http; Webhook Handling // convex/http.ts import { httpRouter } from "convex/server" ; import { httpAction } from "./_generated/server" ; import { internal } from "./_generated/api" ; const http = httpRouter (); // Stripe webhook http. route ({ path : "/webhooks/stripe" , method : "POST" , handler : httpAction ( async (ctx, request) => { const signature = request. headers . get ( "stripe-signature" ); if (!signature) { return new Response ( "Missing signature" , { status : 400 }); } const body = await request. text (); // Verify webhook signature (in action with Node.js) try { await ctx. runAction (internal. stripe . verifyAndProcessWebhook , { body, signature, }); return new Response ( "OK" , { status : 200 }); } catch (error) { console . error ( "Webhook error:" , error); return new Response ( "Webhook error" , { status : 400 }); } }), }); // GitHub webhook http. route ({ path : "/webhooks/github" , method : "POST" , handler : httpAction ( async (ctx, request) => { const event = request. headers . get ( "X-GitHub-Event" ); const signature = request. headers . get ( "X-Hub-Signature-256" ); if (!signature) { return new Response ( "Missing signature" , { status : 400 }); } const body = await request. text (); await ctx. runAction (internal. github . processWebhook , { event : event ?? "unknown" , body, signature, }); return new Response ( "OK" , { status : 200 }); }), }); export default http; Webhook Signature Verification // convex/stripe.ts "use node" ; import { internalAction, internalMutation } from "./_generated/server" ; import { internal } from "./_generated/api" ; import { v } from "convex/values" ; import Stripe from "stripe" ; const stripe = new Stripe (process. env . STRIPE_SECRET_KEY !); export const verifyAndProcessWebhook = internalAction ({ args : { body : v. string (), signature : v. string (), }, returns : v. null (), handler : async (ctx, args) => { const webhookSecret = process. env . STRIPE_WEBHOOK_SECRET !; // Verify signature const event = stripe. webhooks . constructEvent ( args. body , args. signature , webhookSecret ); // Process based on event type switch (event. type ) { case "checkout.session.completed" : await ctx. runMutation (internal. payments . handleCheckoutComplete , { sessionId : event. data . object . id , customerId : event. data . object . customer as string , }); break ; case "customer.subscription.updated" : await ctx. runMutation (internal. subscriptions . handleUpdate , { subscriptionId : event. data . object . id , status : event. data . object . status , }); break ; } return null ; }, }); Authentication in HTTP Actions // convex/http.ts import { httpRouter } from "convex/server" ; import { httpAction } from "./_generated/server" ; import { internal } from "./_generated/api" ; const http = httpRouter (); // API key authentication http. route ({ path : "/api/protected" , method : "GET" , handler : httpAction ( async (ctx, request) => { const apiKey = request. headers . get ( "X-API-Key" ); if (!apiKey) { return new Response ( JSON . stringify ({ error : "Missing API key" }), { status : 401 , headers : { "Content-Type" : "application/json" } } ); } // Validate API key const isValid = await ctx. runQuery (internal. auth . validateApiKey , { apiKey, }); if (!isValid) { return new Response ( JSON . stringify ({ error : "Invalid API key" }), { status : 403 , headers : { "Content-Type" : "application/json" } } ); } // Process authenticated request const data = await ctx. runQuery (internal. data . getProtectedData , {}); return new Response ( JSON . stringify (data), { status : 200 , headers : { "Content-Type" : "application/json" } } ); }), }); // Bearer token authentication http. route ({ path : "/api/user" , method : "GET" , handler : httpAction ( async (ctx, request) => { const authHeader = request. headers . get ( "Authorization" ); if (!authHeader?. startsWith ( "Bearer " )) { return new Response ( JSON . stringify ({ error : "Missing or invalid Authorization header" }), { status : 401 , headers : { "Content-Type" : "application/json" } } ); } const token = authHeader. slice ( 7 ); // Validate token and get user const user = await ctx. runQuery (internal. auth . validateToken , { token }); if (!user) { return new Response ( JSON . stringify ({ error : "Invalid token" }), { status : 403 , headers : { "Content-Type" : "application/json" } } ); } return new Response ( JSON . stringify (user), { status : 200 , headers : { "Content-Type" : "application/json" } } ); }), }); export default http; Calling Mutations and Queries // convex/http.ts import { httpRouter } from "convex/server" ; import { httpAction } from "./_generated/server" ; import { api, internal } from "./_generated/api" ; const http = httpRouter (); http. route ({ path : "/api/items" , method : "POST" , handler : httpAction ( async (ctx, request) => { const body = await request. json (); // Call a mutation const itemId = await ctx. runMutation (internal. items . create , { name : body. name , description : body. description , }); // Query the created item
Keywords that activate this skill. Click one to copy it.

This skill does not provide trigger words.

The downloaded .skill package contains the following fields.
Field Description
formatFormat tag (skill/v1)
skill_idUnique skill ID
nameSkill name
versionVersion
descriptionDescription
categoryCategories (array)
trigger_wordsTrigger words
tagsTags
sourceSource
source_urlSource URL (this page)
exported_atExported at (set per download)
system_promptSystem prompt body
model_configModel config: provider / model / temperature / max_tokens / top_p
examplesExamples
install_guideImport guide for Coze / Dify / Claude / custom frameworks
The same skill can be exported in different platform formats.
.skill Standard format with system_prompt and model_config, ready for any agent framework Download
.skillpro Enhanced format with scripts, tools, dependencies and hooks Download
.json Plain JSON export with system_prompt and model parameters only Download
Coze Markdown with frontmatter, for Coze platform import Download
Dify Dify DSL, import directly after creating an app Download

每日精选 Skill 推荐,免费送到你邮箱

输入邮箱,每天接收一个精选 AI Agent 技能推荐。完全免费,持续更新。

提交后我们会发送一封确认邮件,点击邮件里的链接才会开始收信。

完全免费,取消任意时间。我们不会发送垃圾邮件。