Skill: canuto/codehooks-backend (version 1.0.0)

--- name: codehooks-backend description: Deploy serverless backends for REST APIs, webhooks, data storage, scheduled jobs, queue workers, and autonomous workflows. metadata: { "openclaw": { "emoji": "🪝", "requires": { "bins": ["coho"], "env": ["CODEHOOKS_ADMIN_TOKEN"] } } } --- # Codehooks Backend Skill Give your OpenClaw agent a serverless backend for REST APIs, webhooks, data storage, scheduled jobs, queue workers, and autonomous workflows. ## Your agent can deploy code With this skill, your agent can write JavaScript/TypeScript code and deploy it to a live serverless backend in 5 seconds. No human intervention required — the agent iterates autonomously. Codehooks has a free tier to get started, and paid plans have no extra charges for traffic or API calls — let your agent deploy without worrying about usage costs. ⚠️ **Warning:** This gives your agent the ability to deploy and run code on a live server. Review your agent's actions, set appropriate permissions, and monitor usage. You are responsible for any code your agent deploys. ## What this skill enables - **REST APIs** with automatic OpenAPI/Swagger documentation - **Instant CRUD APIs** using `crudlify()` with schema validation - **Webhook endpoints** that external services can call (Stripe, GitHub, Shopify, etc.) - **Persistent storage** beyond local memory (NoSQL + key-value) - **Background jobs** and scheduled tasks that run 24/7 - **Queue workers** for async processing - **Autonomous workflows** with retries, branching, and state management ## Setup **Human does once:** ```bash npm install -g codehooks coho login coho create openclaw-backend coho add-admintoken ``` Give the admin token to your agent. **Agent uses:** ```bash export CODEHOOKS_ADMIN_TOKEN="your-token-here" coho deploy --admintoken $CODEHOOKS_ADMIN_TOKEN ``` The agent can now deploy code, query data, and manage the backend. --- ## Essential: Load the development context Before building anything, run: ```bash coho prompt ``` This outputs the complete Codehooks development prompt — routing, database, queues, jobs, workflows, and the full codehooks-js API. Copy it into your context to build any backend feature correctly. **macOS shortcut:** ```bash coho prompt | pbcopy ``` ## Understand existing projects Before modifying an existing project, get the full picture: ```bash # Returns JSON with collections, stats, recent deploys, and error logs coho doctor # Describe the app structure — collections, schemas, queues, files coho describe ``` `coho doctor` is the most powerful diagnostic command — it returns structured JSON covering database collections with document counts, deployment history, queue and worker status, and recent error logs. Always run it when joining an existing project or debugging issues. `coho describe` complements doctor by showing the structural overview: what collections exist, their schemas, registered queues, and deployed files. --- ## Commands your agent can use All commands accept `--admintoken $CODEHOOKS_ADMIN_TOKEN` for non-interactive use. Full CLI reference: https://codehooks.io/docs/cli | Command | What it does | |---------|--------------| | `coho prompt` | Get the full development context | | `coho doctor` | Diagnose project state — collections, stats, deploys, error logs | | `coho describe` | Describe app structure — collections, schemas, queues, files | | `coho deploy` | Deploy code (5 seconds to live) | | `coho info --examples` | Get endpoint URLs with cURL examples | | `coho log -f` | Stream logs in real-time | | `coho query -c <collection> -q 'field=value'` | Query the database | | `coho queue-status` | Check queue status | | `coho workflow-status` | Check workflow status | | `coho import -c <collection> --file data.json` | Import data | | `coho export -c <collection>` | Export data | --- ## Code examples ### Instant CRUD API with validation ```javascript import { app } from 'codehooks-js'; import * as Yup from 'yup'; const productSchema = Yup.object({ name: Yup.string().required(), price: Yup.number().positive().required(), category: Yup.string().required() }); // Creates GET, POST, PUT, DELETE endpoints automatically // OpenAPI docs available at /.well-known/openapi app.crudlify({ product: productSchema }); export default app.init(); ``` ### Webhook that stores incoming data ```javascript import { app, Datastore } from 'codehooks-js'; // Allow webhook endpoint without JWT authentication app.auth('/webhook', (req, res, next) => { next(); }); app.post('/webhook', async (req, res) => { const conn = await Datastore.open(); await conn.insertOne('events', { ...req.body, receivedAt: new Date().toISOString() }); res.json({ ok: true }); }); export default app.init(); ``` ### Scheduled job (runs daily at 9am) ```javascript import { app, Datastore } from 'codehooks-js'; app.job('0 9 * * *', async (_, { jobId }) => { console.log(`Running job: ${jobId}`); const conn = await Datastore.open(); const events = await conn.getMany('events', {}).toArray(); console.log('Daily summary:', events.length, 'events'); }); export default app.init(); ``` ### Queue worker for async processing ```javascript import { app, Datastore } from 'codehooks-js'; app.worker('processTask', async (req, res) => { const { task } = req.body.payload; const conn = await Datastore.open(); await conn.updateOne('tasks', { _id: task.id }, { $set: { status: 'completed' } }); res.end(); }); export default app.init(); ``` ### Autonomous workflow (multi-step with retries) ```javascript import { app } from 'codehooks-js'; const workflow = app.createWorkflow('myTask', 'Process tasks autonomously', { begin: async function (state, goto) { console.log('Starting task:', state.taskId); goto('process', state); }, process: async function (state, goto) { // Do work here - workflow handles retries and state state = { ...state, result: 'processed' }; goto('complete', state); }, complete: function (state, goto) { console.log('Done:', state.result); goto(null, state); // End workflow } }); // Agent starts workflow via API app.post('/start', async (req, res) => { const result = await workflow.start(req.body); res.json(result); }); export default app.init(); ``` --- ## Important patterns - **`getMany()` returns a stream** — use `.toArray()` when you need to manipulate data (sort, filter, map) - **Webhook signatures:** Use `req.rawBody` for signature verification, not `req.body` - **No filesystem access:** `fs`, `path`, `os` are not available — this is a serverless environment - **Secrets:** Use `process.env.VARIABLE_NAME` for API keys and secrets - **Static files:** `app.static({ route: '/app', directory: '/public' })` serves static sites from deployed source - **File storage:** `app.storage({ route: '/docs', directory: '/uploads' })` serves uploaded files --- ## Development workflow: Let your agent build new endpoints 1. Agent runs `coho prompt` and loads the development context 2. For existing projects, agent runs `coho doctor` and `coho describe` to understand what's deployed 3. Agent writes code using codehooks-js patterns 4. Agent runs `coho deploy` (5 seconds to live) 5. Agent verifies with `coho log -f` or tests endpoints with `coho info --examples` 6. Agent iterates — the fast deploy loop enables rapid development --- ## When to use this skill - You need a reliable webhook URL for Stripe, GitHub, Shopify, etc. - You want persistent storage outside your local machine - You need scheduled jobs that run even when your device is off - You want to offload sensitive API integrations to a sandboxed environment - You need queues for async processing - You want autonomous multi-step workflows that run independently with retries --- ## Resources - **Documentation:** https://codehooks.io/docs - **CLI reference:** https://codehooks.io/docs/cli - **AI prompt:** Run `coho prompt` or visit https://codehooks.io/llms.txt - **Templates:** https://github.com/RestDB/codehooks-io-templates - **MCP Server:** https://github.com/RestDB/codehooks-mcp-server

path: CLAUDE.md
content: # CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What This Is

This is an OpenClaw skill package (`codehooks-openclaw-skill`) — not a runnable application. It provides `SKILL.md` (the skill definition for OpenClaw agents) and `examples/` (ready-to-use code templates). There is no build system, test suite, or package.json — the repo is documentation and example code that teaches OpenClaw agents how to use the Codehooks serverless platform.

## Repository Structure

- `SKILL.md` — The OpenClaw skill definition file. This is the core artifact. It follows the OpenClaw skill format with YAML frontmatter (`name`, `description`, `metadata`) and markdown body containing instructions, CLI commands, and code examples.
- `README.md` — User-facing documentation for the skill package.
- `examples/` — JavaScript code templates using `codehooks-js`:
  - `webhook-handler.js` — Stripe webhook with signature verification via `webhook-verify`
  - `daily-job.js` — Cron-scheduled job (`app.job()`)
  - `queue-worker.js` — Async queue processing (`app.worker()` + `conn.enqueue()`)
  - `workflow-automation.js` — Multi-step autonomous workflow (`app.createWorkflow()`)

## Key Concepts

**Codehooks-js patterns** — All examples follow the same structure:
1. Import `app` (and `Datastore`) from `codehooks-js`
2. Register handlers (routes, jobs, workers, workflows)
3. Export `app.init()` as default

**Auth bypass for webhooks** — External webhook endpoints need `app.auth('/path', (req, res, next) => next())` to skip JWT authentication.

**SKILL.md frontmatter** — The `metadata` field uses nested JSON: `{ "openclaw": { "emoji": "...", "requires": { "bins": [...], "env": [...] } } }`. The `requires.bins` lists CLI tools (`coho`) and `requires.env` lists required environment variables (`CODEHOOKS_ADMIN_TOKEN`).

## Editing Guidelines

When modifying `SKILL.md`, keep code examples self-contained and deployable — agents copy-paste them directly. The `coho prompt` command provides the full codehooks-js API reference, so `SKILL.md` only needs common patterns, not exhaustive API docs.

When adding examples, follow the existing pattern: single-file, ES module imports, export `app.init()`, and include comments explaining the "why" (e.g., auth bypass).


path: README.md
content: # codehooks-openclaw-skill

Give your OpenClaw agent a serverless backend for REST APIs, webhooks, data storage, scheduled jobs, queue workers, and autonomous workflows.

## Why this exists

OpenClaw agents run locally. That's great for privacy, but tricky when you need:

- REST APIs with automatic OpenAPI documentation
- Webhook endpoints that work when your Mac is asleep
- Persistent storage beyond local files
- Scheduled jobs that run 24/7
- Reliable integrations with Stripe, Shopify, GitHub
- Multi-step workflows that run autonomously with retries and state management

This skill gives your agent everything it needs to deploy and manage serverless backends.

## Quick start

**Human does once:**
```bash
npm install -g codehooks
coho login
coho create my-backend
coho add-admintoken
```

Give the admin token to your agent.

**Agent uses:**
```bash
export CODEHOOKS_ADMIN_TOKEN="your-token-here"
coho deploy --admintoken $CODEHOOKS_ADMIN_TOKEN
```

Your agent now has a live webhook URL, database, and job runner. Codehooks has a free tier to get started, and paid plans have no extra charges for traffic or API calls.

## Give your agent the full context

Run this to get the complete development prompt:

```bash
coho prompt
```

This outputs everything your agent needs to build with codehooks-js — routes, database operations, queues, jobs, workflows. Feed it into your agent's context.

**macOS shortcut:**
```bash
coho prompt | pbcopy
```

## What's included

- **SKILL.md** — The skill file for OpenClaw
- **examples/** — Ready-to-use code templates
  - `webhook-handler.js` — Webhook with signature verification
  - `daily-job.js` — Scheduled task example
  - `queue-worker.js` — Async queue processing
  - `workflow-automation.js` — Multi-step autonomous workflows

## Commands reference

All commands accept `--admintoken $CODEHOOKS_ADMIN_TOKEN` for non-interactive use. [Full CLI reference](https://codehooks.io/docs/cli)

| Command | Description |
|---------|-------------|
| `coho prompt` | Get full development context |
| `coho deploy` | Deploy code (5 seconds) |
| `coho info --examples` | Get endpoint URL with example API calls |
| `coho log -f` | Stream logs |
| `coho query -c <collection> -q 'field=value'` | Query database |
| `coho import -c <collection> --file data.json` | Import data |
| `coho export -c <collection>` | Export data |

## Example: Stripe webhook handler

```javascript
import { app, Datastore } from 'codehooks-js';
import { verify } from 'webhook-verify';

// Allow webhook endpoint without JWT authentication
app.auth('/stripe-webhook', (req, res, next) => next());

app.post('/stripe-webhook', async (req, res) => {
  // Verify signature using rawBody (essential for HMAC validation)
  const isValid = verify('stripe', req.rawBody, req.headers, process.env.STRIPE_WEBHOOK_SECRET);
  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  const conn = await Datastore.open();
  await conn.insertOne('payments', {
    event: req.body,
    receivedAt: new Date().toISOString()
  });
  res.json({ received: true });
});

export default app.init();
```

## Workflows for autonomous agents

Workflows let your agent kick off multi-step processes that run independently:

```javascript
// Agent starts a workflow
const { workflowId } = await fetch('/workflow/start', {
  method: 'POST',
  body: JSON.stringify({
    data: taskData,
    callbackUrl: 'https://my-agent/webhook' // Get notified on completion
  })
}).then(r => r.json());

// Workflow runs autonomously with retries, state persistence, and error handling
// Agent gets notified via callback when done
```

See `examples/workflow-automation.js` for a complete example.

## Resources

- [How to Give Your OpenClaw Agent a Backend](https://codehooks.io/blog/openclaw-backend) — Blog post walkthrough
- [Codehooks Documentation](https://codehooks.io/docs)
- [OpenAPI/Swagger Docs](https://codehooks.io/docs/openapi-swagger-docs)
- [AI development prompt](https://codehooks.io/docs/chatgpt-backend-api-prompt)
- [webhook-verify](https://www.npmjs.com/package/webhook-verify) — Signature verification for Stripe, GitHub, Shopify, etc.
- [More templates](https://github.com/RestDB/codehooks-io-templates)
- [MCP Server](https://github.com/RestDB/codehooks-mcp-server)

## License

MIT

---

**Your agent writes the code. Codehooks runs it.**


path: examples/daily-job.js
content: import { app, Datastore } from 'codehooks-js';

// Run daily at 9am
app.job('0 9 * * *', async (_, { jobId }) => {
  console.log(`Running daily summary job: ${jobId}`);
  const conn = await Datastore.open();
  const events = await conn.getMany('events', {}).toArray();
  console.log('Daily summary:', events.length, 'events');
});

export default app.init();


path: examples/queue-worker.js
content: import { app, Datastore } from 'codehooks-js';

app.worker('processTask', async (req, res) => {
  const { task } = req.body.payload;
  console.log('Processing task:', task.id);

  const conn = await Datastore.open();
  await conn.updateOne('tasks', { _id: task.id }, { $set: { status: 'completed' } });

  res.end();
});

// Endpoint to enqueue tasks
app.post('/tasks', async (req, res) => {
  const conn = await Datastore.open();
  const task = await conn.insertOne('tasks', {
    ...req.body,
    status: 'pending',
    createdAt: new Date()
  });
  await conn.enqueue('processTask', { task });
  res.json(task);
});

export default app.init();


path: examples/webhook-handler.js
content: import { app, Datastore } from 'codehooks-js';
import { verify } from 'webhook-verify';

// Allow webhook endpoint without JWT authentication
app.auth('/webhook/*', (req, res, next) => {
  next();
});

app.post('/webhook/stripe', async (req, res) => {
  // Verify the webhook signature using webhook-verify
  // Supports: stripe, github, shopify, slack, twilio, and more
  const isValid = verify('stripe', req.rawBody, req.headers, process.env.STRIPE_WEBHOOK_SECRET);
  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  const conn = await Datastore.open();
  await conn.insertOne('events', {
    ...req.body,
    receivedAt: new Date().toISOString()
  });
  res.json({ received: true });
});

export default app.init();


path: examples/workflow-automation.js
content: import { app, Datastore } from 'codehooks-js';

// Workflow for autonomous task processing
// Agent starts workflow, it runs independently, agent gets notified on completion
const taskWorkflow = app.createWorkflow(
  'autonomousTask',
  'Multi-step task processor for autonomous agents',
  {
    // Step 1: Initialize and validate the task
    begin: async function (state, goto) {
      console.log('Starting workflow for task:', state.taskId);
      const conn = await Datastore.open();

      const task = await conn.findOneOrNull('tasks', state.taskId);
      if (!task) {
        state.error = 'Task not found';
        goto('failed', state);
        return;
      }

      await conn.updateOne('tasks', state.taskId, {
        $set: { status: 'processing', startedAt: new Date() }
      });

      state.task = task;
      state.attempts = 0;
      goto('process', state);
    },

    // Step 2: Process the task (with retry logic)
    process: async function (state, goto) {
      state.attempts++;
      console.log(`Processing attempt ${state.attempts} for task:`, state.taskId);

      try {
        // Simulate task processing - replace with actual logic
        const result = await processTask(state.task);
        state.result = result;
        goto('complete', state);
      } catch (error) {
        state.lastError = error.message;

        if (state.attempts < 3) {
          console.log('Retrying after error:', error.message);
          goto('process', state); // Retry
        } else {
          goto('failed', state);
        }
      }
    },

    // Step 3a: Task completed successfully
    complete: async function (state, goto) {
      const conn = await Datastore.open();
      await conn.updateOne('tasks', state.taskId, {
        $set: {
          status: 'completed',
          result: state.result,
          completedAt: new Date()
        }
      });

      state.finalStatus = 'completed';
      goto(null, state); // End workflow
    },

    // Step 3b: Task failed after retries
    failed: async function (state, goto) {
      const conn = await Datastore.open();
      await conn.updateOne('tasks', state.taskId, {
        $set: {
          status: 'failed',
          error: state.error || state.lastError,
          failedAt: new Date(),
          attempts: state.attempts
        }
      });

      state.finalStatus = 'failed';
      goto(null, state); // End workflow
    }
  },
  {
    collectionName: 'workflow_state',
    timeout: 60000,
    maxStepCount: 10,
    workers: 3,
    steps: {
      process: {
        timeout: 30000,
        maxRetries: 3
      }
    }
  }
);

// Notify when workflow completes - agent can poll or receive webhook
taskWorkflow.on('completed', async (data) => {
  console.log('Workflow completed:', data);

  // Optionally notify the agent via webhook callback
  if (data.state?.callbackUrl) {
    try {
      await fetch(data.state.callbackUrl, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          workflowId: data.workflowId,
          status: data.state.finalStatus,
          result: data.state.result
        })
      });
    } catch (err) {
      console.error('Callback failed:', err.message);
    }
  }
});

// Helper function - replace with actual task logic
async function processTask(task) {
  // Simulate async work
  await new Promise(resolve => setTimeout(resolve, 1000));
  return { processed: true, data: task.data };
}

// API: Start a new workflow
app.post('/workflow/start', async (req, res) => {
  const conn = await Datastore.open();

  // Create the task
  const task = await conn.insertOne('tasks', {
    ...req.body,
    status: 'pending',
    createdAt: new Date()
  });

  // Start the workflow
  const workflow = await taskWorkflow.start({
    taskId: task._id,
    callbackUrl: req.body.callbackUrl // Optional: agent's webhook URL
  });

  res.json({
    taskId: task._id,
    workflowId: workflow._id,
    status: 'started'
  });
});

// API: Check workflow/task status
app.get('/workflow/status/:taskId', async (req, res) => {
  const conn = await Datastore.open();
  const task = await conn.findOneOrNull('tasks', req.params.taskId);

  if (!task) {
    return res.status(404).json({ error: 'Task not found' });
  }

  res.json(task);
});

export default app.init();


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (MEDIUM)