Skip to main content
AI/MLjeremylongshore

clickup-deploy-integration

'Deploy ClickUp API integrations to Vercel, Fly.io, and Cloud Run with

Stars
2,267
Source
jeremylongshore/claude-code-plugins-plus-skills
Updated
2026-05-31
Slug
jeremylongshore--claude-code-plugins-plus-skills--clickup-deploy-integration
View on GitHubRaw SKILL.md

// install — copy + paste into any project

mkdir -p .claude/skills && curl -fsSL https://raw.githubusercontent.com/jeremylongshore/claude-code-plugins-plus-skills/HEAD/plugins/saas-packs/clickup-pack/skills/clickup-deploy-integration/SKILL.md -o .claude/skills/clickup-deploy-integration.md

Drops the SKILL.md into .claude/skills/clickup-deploy-integration.md. Works with Claude Code, Cursor, and any agent that loads SKILL.md files from .claude/skills/.

ClickUp Deploy Integration

Overview

Deploy ClickUp-powered applications with secure token management. ClickUp API v2 is a standard REST API -- your app just needs CLICKUP_API_TOKEN available at runtime and outbound HTTPS to api.clickup.com.

Required Environment Variables

Variable Purpose Required
CLICKUP_API_TOKEN Personal API token or OAuth access token Yes
CLICKUP_TEAM_ID Workspace ID for scoped operations Recommended
CLICKUP_WEBHOOK_SECRET For webhook signature validation If using webhooks

Vercel Deployment

# Add secrets
vercel env add CLICKUP_API_TOKEN production
vercel env add CLICKUP_TEAM_ID production

# Deploy
vercel --prod
// api/clickup/tasks.ts (Vercel serverless function)
export async function GET(request: Request) {
  const listId = new URL(request.url).searchParams.get('list_id');
  if (!listId) return Response.json({ error: 'list_id required' }, { status: 400 });

  const response = await fetch(
    `https://api.clickup.com/api/v2/list/${listId}/task?archived=false`,
    { headers: { 'Authorization': process.env.CLICKUP_API_TOKEN! } }
  );

  if (!response.ok) {
    return Response.json({ error: 'ClickUp API error' }, { status: response.status });
  }

  const data = await response.json();
  return Response.json(data.tasks);
}

Fly.io Deployment

# Set secrets (encrypted at rest)
fly secrets set CLICKUP_API_TOKEN=pk_12345678_YOUR_TOKEN
fly secrets set CLICKUP_TEAM_ID=1234567

# Deploy
fly deploy
# fly.toml
app = "my-clickup-app"
primary_region = "iad"

[env]
  NODE_ENV = "production"

[http_service]
  internal_port = 3000
  force_https = true
  auto_stop_machines = true
  auto_start_machines = true

[[http_service.checks]]
  grace_period = "5s"
  interval = "30s"
  method = "GET"
  path = "/health"
  timeout = "5s"

Google Cloud Run

# Store token in Secret Manager
echo -n "pk_12345678_YOUR_TOKEN" | gcloud secrets create clickup-api-token --data-file=-

# Deploy with secret mounted as env var
gcloud run deploy clickup-service \
  --image gcr.io/$PROJECT_ID/clickup-service \
  --region us-central1 \
  --set-secrets=CLICKUP_API_TOKEN=clickup-api-token:latest \
  --set-env-vars=CLICKUP_TEAM_ID=1234567 \
  --allow-unauthenticated

Health Check Endpoint

// src/health.ts — verify ClickUp connectivity
export async function healthCheck() {
  const start = Date.now();

  try {
    const response = await fetch('https://api.clickup.com/api/v2/user', {
      headers: { 'Authorization': process.env.CLICKUP_API_TOKEN! },
      signal: AbortSignal.timeout(5000),
    });

    const remaining = response.headers.get('X-RateLimit-Remaining');

    return {
      status: response.ok ? 'healthy' : 'degraded',
      clickup: {
        connected: response.ok,
        httpStatus: response.status,
        latencyMs: Date.now() - start,
        rateLimitRemaining: remaining ? parseInt(remaining) : null,
      },
    };
  } catch (error) {
    return {
      status: 'unhealthy',
      clickup: {
        connected: false,
        error: error instanceof Error ? error.message : 'Unknown',
        latencyMs: Date.now() - start,
      },
    };
  }
}

Webhook Endpoint for Deployments

// api/webhooks/clickup.ts — receive ClickUp webhook events
export async function POST(request: Request) {
  const body = await request.json();

  // ClickUp webhook payloads include event type and history_items
  const { event, task_id, history_items } = body;

  // Process async (respond within 30s or ClickUp marks as failed)
  // Queue for processing if needed
  console.log(`ClickUp event: ${event} for task ${task_id}`);

  return Response.json({ received: true });
}

Error Handling

Issue Cause Solution
Secret not found in runtime Missing env config Verify with platform CLI
Cold start timeout ClickUp API slow on first call Set min instances = 1
Health check fails Token rotated Update secret, redeploy
Webhook endpoint 5xx Slow processing Respond 200 immediately, process async

Prerequisites

  • Environment-specific runtime identity, secret references, and deployment owner
  • Protected release gate, health checks, monitoring, and feature-disable path
  • Queue/idempotency design for asynchronous ClickUp/webhook work

Instructions

Deploy first to staging, validate runtime identity, health, one bounded API operation, and webhook receipt with synthetic data, then promote through the approved environment gate. Keep processing asynchronous and idempotent; on a failed health, secret, or authorization check, roll back rather than widening permissions or exposing an endpoint.

Output

Publish a deployment receipt with release ID, environment, secret references, health/callback results, queue state, monitoring, approval, and rollback decision. Exclude tokens, webhook URLs, task bodies, and raw event payloads.

Examples

Deploy a staging worker with a dedicated identity, submit a synthetic event, verify its durable idempotency record and redacted health result, then promote only after the production gate approves. If the endpoint returns 5xx or lacks its secret, disable the release and fix configuration before retrying.

Resources

Next Steps

For webhook event handling, see clickup-webhooks-events.