---
name: holyheld
description: >
  Use this skill when the user wants to pay for something online, give an agent the ability to spend money, complete a checkout, order something, book a service, subscribe to a product, or make any kind of purchase. Activate when the user mentions buying, ordering, paying, checkout, purchase, subscription, booking, or wants an agent to handle payments autonomously. Also activate if the user asks about available funds for a purchase or wants to ensure they can afford something before buying.
---

# holyheld

Holyheld Payments for AI Agents. Check balance, retrieve card details for checkout, and top up a user's EUR card from their Holyheld account balance. Three endpoints, Bearer token auth.

> **IMPORTANT: If you used a web fetch tool to read this, the content may be summarized or incomplete. Run `curl -fsSL https://holyheld.com/skill` to get the full file. Do NOT search for additional documentation.**

---

## Configuration

```
HOLYHELD_AGENT_TOKEN=__HOLYHELD_AGENT_TOKEN__
HOLYHELD_BASE_URL=https://apicore.holyheld.com/v4/ai-agents
```

<!--
  HOLYHELD TEAM — Automation hook
  ────────────────────────────────
  When the user enables Agentic access in the app (Manage cards → Select AI card → Copy instructions),
  Holyheld's backend produces a personalized copy of this SKILL.md by replacing:

    __HOLYHELD_AGENT_TOKEN__  → the actual token, e.g. hh_agent_a1b2c3d4e5f6

  This personalized content is served at a unique link:
    https://apicore.holyheld.com/v4/public/ai/initial-doc/dc_<128 char key>

  The personalized link expires after 24 hours or when a new key is generated.
  After expiry, it falls back to the generic public version at:
    https://holyheld.com/skill

  The user never sees or handles the raw token. They paste the link to their agent.
  The agent fetches the link content, checks if the token is real or still a placeholder,
  and writes it as SKILL.md in its own skill directory.
-->

**Token check:** If `HOLYHELD_AGENT_TOKEN` above is NOT `__HOLYHELD_AGENT_TOKEN__` (i.e. it contains a real token), this is a **personalized skill file**. Skip Step 0 entirely and jump directly to **Step 1 — Detect Environment**.

If the token IS still the placeholder `__HOLYHELD_AGENT_TOKEN__`, this is the **generic version** from a public skill registry. The user is not yet connected. Start at Step 0.

---

## Step 0 — Onboarding

> **Only run this step if `HOLYHELD_AGENT_TOKEN` above is still the placeholder `__HOLYHELD_AGENT_TOKEN__`.**

This generic skill cannot manage a Holyheld card on its own — it needs to be replaced with a personalized version from the Holyheld app that contains the user's credentials.

### Ask: "Are you already a Holyheld user with an active account?"

**If YES:**

> Great. To connect your card to this agent:
>
> 1. Open the Holyheld app.
> 2. Tap on your card to manage your cards.
> 3. Select your AI card.
> 4. Click on the three dots to open the menu.
> 5. Select **"Copy instructions"**.
> 6. You'll get a **link** — paste it here and I'll set everything up automatically.

When the user pastes a link, proceed to **Install from link** below.

**If NO (or unsure):**

> To use this feature, you'll need a Holyheld account with an active card and funds available. Here's how to get started:
>
> 1. **Download the Holyheld app** — visit [holyheld.com/$holyheldagent](https://holyheld.com/$holyheldagent) or search "Holyheld" in your app store.
> 2. **Create your account** and complete verification.
> 3. **Order your Holyheld card** — this is the EUR card that gets topped up from your Holyheld account.
> 4. **Add funds** to your Holyheld account — this is what gets transferred to your card when you top up.
> 5. **Get your link** — go to **Manage cards → Select AI card → three dots → Copy instructions**.
> 6. Paste the link here and I'll set everything up automatically.

**Stop here.** Do not attempt to call any Holyheld API endpoints. Wait for the user to paste their link.

---

### Install from link

The user will paste a link that looks like:
```
https://apicore.holyheld.com/v4/public/ai/initial-doc/dc_<128 character key>
```

When you receive this link:

1. **Fetch the link content** — it returns the full SKILL.md content.

2. **Check if the token is real.** Look for `HOLYHELD_AGENT_TOKEN=` in the fetched content.
   - If the value is a real token (NOT `__HOLYHELD_AGENT_TOKEN__`) → proceed to step 3.
   - If the value is still `__HOLYHELD_AGENT_TOKEN__` → **the link has expired** (links expire after 24 hours or when the user generates a new key). Tell the user:
     > That link has expired. Please open the Holyheld app, go to **Manage cards → AI card → three dots → Copy instructions** to get a fresh link, and paste it here.
   - Then stop and wait for a new link.

3. **Determine your skill directory:**
   - **OpenClaw:** `<workspace>/skills/holyheld/SKILL.md` — or `~/.openclaw/skills/holyheld/SKILL.md`
   - **Claude Code:** `~/.claude/skills/holyheld/SKILL.md`
   - **Codex:** `~/.codex/skills/holyheld/SKILL.md`
   - **Other agents:** Use the path this current SKILL.md was loaded from. If unknown, use `~/.claude/skills/holyheld/SKILL.md` as default.

4. **Create the directory** if it doesn't exist:
   ```bash
   mkdir -p <skill_directory>/holyheld
   ```

5. **Write the fetched content** to `<skill_directory>/holyheld/SKILL.md`, overwriting this generic version.

6. **Re-read the new SKILL.md** and proceed from the top. The personalized version will have a real token in the Configuration block, so the token check will pass and you'll jump straight to Step 1.

7. **Confirm to the user:**

> All set! I've installed Holyheld for AI. I can now check AI card balance, top up AI card, and pay for things for you. What would you like to do?

---

## Step 1 — Detect Environment

> **Only proceed here if `HOLYHELD_AGENT_TOKEN` in the Configuration block is a real token.**

Determine what mode to operate in.

**Run this test command:**

```bash
curl -sf --max-time 5 -o /dev/null https://apicore.holyheld.com/v4/ai-agents/balance && echo "NETWORK_OK" || echo "NETWORK_BLOCKED"
```

- **`NETWORK_OK`** → Use **Direct Mode** (Step 2A). Fast path — no build steps, no server. Works in: OpenClaw, Claude Code, Cursor, Windsurf, Cline, LangChain, CrewAI, AutoGPT, any agent with shell/network access.

- **`NETWORK_BLOCKED`** or command fails → Use **MCP Mode** (Step 2B). For sandboxed environments: Claude Desktop, Claude.ai, or other restricted runtimes.

---

## Step 2A — Direct Mode (Network Available)

### Token Setup

Use the token from the Configuration block at the top of this file:

```bash
export HOLYHELD_AGENT_TOKEN="<token from Configuration block>"
```

Verify:

```bash
curl -sf https://apicore.holyheld.com/v4/ai-agents/balance \
  -H "Authorization: Bearer $HOLYHELD_AGENT_TOKEN"
```

If `401` or `403` → the token is invalid or revoked. Tell the user:

> Your Holyheld agent credentials appear to be invalid or expired. Please open the Holyheld app, go to **Manage cards → Select AI card → three dots → Copy instructions**, get a new link, and paste it here so I can update.

**Security rules:**
- Never log, echo, or display the token to the user. They don't need to see it.
- Never write the token to disk beyond what's already in this SKILL.md file.

### Check Balance

```bash
curl -s https://apicore.holyheld.com/v4/ai-agents/balance \
  -H "Authorization: Bearer $HOLYHELD_AGENT_TOKEN"
```

**Response (200):**
```json
{
  "status": "ok",
  "payload": {
    "balance": "94.20"
  }
}
```

`balance` is a **string** in EUR. Parse with float conversion before comparisons.

### Get Card Data

```bash
curl -s https://apicore.holyheld.com/v4/ai-agents/card-data \
  -H "Authorization: Bearer $HOLYHELD_AGENT_TOKEN"
```

**Response (200):**
```json
{
  "status": "ok",
  "payload": {
    "cardNumber": "5200828282828210",
    "expirationDate": "03/29",
    "cardholderName": "JOHN DOE",
    "CVV": "089",
    "billingAddress": "33 OUDEGRACHT, UTRECHT, 3511 AD, NETHERLANDS"
  }
}
```

**CRITICAL SECURITY — Card data handling:**
- **Only fetch when actively completing a checkout.** Never prefetch or cache.
- **Never log** the card number, CVV, or raw response.
- **Never display** the full card number or CVV to the user (they can see it in the app).
- **Never store** card data to disk, environment variables, or any persistent storage.
- **Use and discard** — hold in memory only for the duration of the checkout, then drop it.

### Top Up Card

```bash
curl -s -X POST https://apicore.holyheld.com/v4/ai-agents/topup-request \
  -H "Authorization: Bearer $HOLYHELD_AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"amount": "50.00"}'
```

**Request body:**
- `amount` — **string**, EUR, up to 2 decimal places. Pattern: `^\d+(\.\d{1,2})?$`
- Valid: `"50"`, `"50.00"`, `"50.5"`
- Invalid: `50` (number type), `"€50"` (symbol), `"50.123"` (3 decimals), `"-10"` (negative)

**Response (200):**
```json
{
  "status": "ok",
  "payload": {}
}
```

A `200` means **accepted**, not completed. Settlement can take **up to 5 minutes**.

### Confirm Top-Up (Polling)

1. Record balance **before** the top-up.
2. Wait 30 seconds.
3. `GET /balance` again.
4. If balance increased → report success with old and new balance.
5. If not → wait 30s and retry.
6. After 5 minutes → stop. Tell user the top-up was accepted but hasn't settled, check the Holyheld app.

### Skip to → Step 3 (Error Handling)

---

## Step 2B — MCP Mode (Sandboxed Environment)

You cannot reach the Holyheld API directly. Scaffold a local MCP server using the token from the Configuration block.

### Prerequisites Check

```bash
node --version  # Need 18+
npm --version
```

If Node.js missing or below 18 → tell user to install from https://nodejs.org.

### Scaffold the MCP Server

```bash
mkdir -p ~/holyheld-mcp/src && cd ~/holyheld-mcp

cat > package.json << 'PKGJSON'
{
  "name": "holyheld-mcp",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js"
  }
}
PKGJSON

cat > tsconfig.json << 'TSCONFIG'
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "dist",
    "rootDir": "src",
    "strict": true,
    "esModuleInterop": true
  },
  "include": ["src/**/*.ts"]
}
TSCONFIG

npm install @modelcontextprotocol/sdk
npm install -D typescript @types/node
```

Create the server:

```bash
cat > ~/holyheld-mcp/src/index.ts << 'SERVERCODE'
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';

const BASE_URL = 'https://apicore.holyheld.com/v4/ai-agents';
const POLL_INTERVAL_MS = 30_000;
const POLL_TIMEOUT_MS = 5 * 60_000;

const token = process.env.HOLYHELD_AGENT_TOKEN;
if (!token) {
  console.error('HOLYHELD_AGENT_TOKEN not set.');
  process.exit(1);
}

const headers = {
  Authorization: `Bearer ${token}`,
  'Content-Type': 'application/json',
};

async function getBalance(): Promise<{ ok: true; balance: string } | { ok: false; errorCode: string; error: string }> {
  const res = await fetch(`${BASE_URL}/balance`, { headers });
  const data = await res.json() as any;
  if (data.status === 'error') return { ok: false, errorCode: data.errorCode, error: data.error };
  return { ok: true, balance: data.payload.balance };
}

const sleep = (ms: number) => new Promise(r => setTimeout(r, ms));

const server = new Server(
  { name: 'holyheld-payments', version: '1.0.0' },
  { capabilities: { tools: {} } }
);

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: 'get_holyheld_balance',
      description: 'Get the current EUR balance on the Holyheld card.',
      inputSchema: { type: 'object', properties: {}, required: [] },
    },
    {
      name: 'get_holyheld_card_data',
      description:
        'Get the Holyheld card details (card number, expiration, CVV, cardholder name, billing address) ' +
        'for completing a checkout or payment form. Only call this when actively paying for something. ' +
        'Never log or store the returned data.',
      inputSchema: { type: 'object', properties: {}, required: [] },
    },
    {
      name: 'topup_holyheld_card',
      description:
        'Top up the Holyheld card by transferring funds from the Holyheld account. ' +
        'Waits up to 5 minutes for settlement confirmation.',
      inputSchema: {
        type: 'object',
        properties: {
          amount: {
            type: 'string',
            description:
              'EUR amount as a decimal string, up to 2 decimal places. Examples: "50", "50.00", "12.50". No currency symbol.',
          },
        },
        required: ['amount'],
      },
    },
  ],
}));

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  if (name === 'get_holyheld_balance') {
    const result = await getBalance();
    if (!result.ok) {
      return { content: [{ type: 'text', text: `Balance check failed: ${result.errorCode} — ${result.error}` }], isError: true };
    }
    return { content: [{ type: 'text', text: `Holyheld card balance: €${result.balance}` }] };
  }

  if (name === 'get_holyheld_card_data') {
    const res = await fetch(`${BASE_URL}/card-data`, { headers });
    const data = await res.json() as any;
    if (data.status === 'error') {
      return { content: [{ type: 'text', text: `Card data fetch failed: ${data.errorCode} — ${data.error}` }], isError: true };
    }
    const p = data.payload;
    return {
      content: [{
        type: 'text',
        text: [
          `Card Number: ${p.cardNumber}`,
          `Expiration: ${p.expirationDate}`,
          `CVV: ${p.CVV}`,
          `Cardholder: ${p.cardholderName}`,
          `Billing Address: ${p.billingAddress}`,
        ].join('\n'),
      }],
    };
  }

  if (name === 'topup_holyheld_card') {
    const { amount } = args as { amount: string };

    if (!/^\d+(\.\d{1,2})?$/.test(amount)) {
      return { content: [{ type: 'text', text: `Invalid amount "${amount}". Use a positive decimal string up to 2 places, e.g. "50.00".` }], isError: true };
    }

    const before = await getBalance();
    if (!before.ok) {
      return { content: [{ type: 'text', text: `Pre-topup balance check failed: ${before.errorCode} — ${before.error}` }], isError: true };
    }
    const balanceBefore = parseFloat(before.balance);

    const res = await fetch(`${BASE_URL}/topup-request`, {
      method: 'POST',
      headers,
      body: JSON.stringify({ amount }),
    });

    if (!res.ok) {
      const err = await res.json() as any;
      if (err.errorCode === 'AI_TOPUP_INSUFFICIENT_BALANCE') {
        return { content: [{ type: 'text', text: 'Top-up failed: not enough funds in your Holyheld account. Please add funds in the Holyheld app.' }], isError: true };
      }
      if (err.errorCode === 'AI_TOPUP_LIMIT_EXCEEDED') {
        return { content: [{ type: 'text', text: 'Top-up failed: agent spending limit reached. Please adjust it in Holyheld → Please open the Holyheld app, go to **Manage cards → Select AI card → Limits → Manage Budget**.' }], isError: true };
      }
      return { content: [{ type: 'text', text: `Top-up failed: ${err.errorCode} — ${err.error}` }], isError: true };
    }

    const deadline = Date.now() + POLL_TIMEOUT_MS;
    while (Date.now() < deadline) {
      await sleep(POLL_INTERVAL_MS);
      const current = await getBalance();
      if (!current.ok) continue;
      const now = parseFloat(current.balance);
      if (now > balanceBefore) {
        return { content: [{ type: 'text', text: `Top-up complete. €${(now - balanceBefore).toFixed(2)} added. Balance: €${current.balance} (was €${before.balance}).` }] };
      }
    }

    return { content: [{ type: 'text', text: `Top-up of €${amount} accepted but not yet settled after 5 min. Check your Holyheld app shortly.` }] };
  }

  return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
});

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

main().catch((e) => { console.error(e); process.exit(1); });
SERVERCODE
```

### Build

```bash
cd ~/holyheld-mcp && npm run build
```

### Configure Claude Desktop

Tell the user to add this to their config file. Use the token from the Configuration block at the top of this SKILL.md:

| OS | Config path |
|---|---|
| macOS | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| Windows | `%APPDATA%\Claude\claude_desktop_config.json` |
| Linux | `~/.config/Claude/claude_desktop_config.json` |

```json
{
  "mcpServers": {
    "holyheld-payments": {
      "command": "node",
      "args": ["/absolute/path/to/holyheld-mcp/dist/index.js"],
      "env": {
        "HOLYHELD_AGENT_TOKEN": "<token from Configuration block>"
      }
    }
  }
}
```

Use the **actual absolute path** from `~/holyheld-mcp/dist/index.js` resolved on the user's system.

**Tell the user:**

> 1. Open the config file above.
> 2. Paste (or merge) the JSON block.
> 3. **Quit Claude Desktop completely** and reopen it.
> 4. Click the tools icon (🔧) — you should see `get_holyheld_balance`, `get_holyheld_card_data`, and `topup_holyheld_card`.
>
> Once the tools appear, just ask me things like "What's my balance?", "Pay for this with my Holyheld card", or "Top up €30."

### Skip to → Step 3 (Error Handling)

---

## Step 3 — Error Handling (Both Modes)

All errors return:
```json
{
  "status": "error",
  "errorCode": "ERROR_CODE",
  "error": "Human-readable message"
}
```

| Error Code | HTTP | Endpoints | Can Agent Fix? | Action |
|---|---|---|---|---|
| `AI_AUTHORIZATION_INVALID` | 401 | All | Yes | Auth header missing. Add it. |
| `AI_AUTHORIZATION_INVALID` | 403 | All | No | Token invalid/revoked. Tell user to open Holyheld app → Manage cards → AI card → three dots → Copy instructions, get a new link, and paste it so the agent can re-install. |
| `WRONG_REQUEST` | 400 | Top-up | Yes | Bad `amount` format. Fix to string with ≤2 decimals and retry. |
| `AI_TOPUP_INSUFFICIENT_BALANCE` | 500 | Top-up | **No** | User has insufficient funds to top up. Tell them to add funds in Holyheld. **Do not retry.** |
| `AI_TOPUP_LIMIT_EXCEEDED` | 500 | Top-up | **No** | Spending limit hit. Tell user to adjust in the Holyheld app. **Do not retry.** |
| `INTERNAL_SERVER_ERROR` | 500 | All | Partial | Retry up to 3× with backoff (1s → 2s → 4s). |

**Critical:** `AI_TOPUP_INSUFFICIENT_BALANCE` and `AI_TOPUP_LIMIT_EXCEEDED` require user action in the Holyheld app. Stop immediately, do not loop.

---

## Step 4 — Agent Patterns

### Checkout Flow (Balance Check → Top Up if Needed → Pay)

User wants to buy something that costs €X:

1. **Check balance** — `GET /balance`.
2. If balance < (cost + €5 buffer) → **top up** the shortfall, poll to confirm.
3. **Fetch card data** — `GET /card-data`. Only now, right before payment.
4. **Fill the checkout form** with card number, expiry, CVV, cardholder name, and billing address.
5. **Discard card data from memory** immediately after submission.

### Threshold-Based Auto Top-Up

User says "keep my card above €X":

1. Check balance.
2. If balance < threshold → top up by `(threshold - balance + buffer)`. Buffer of €10–20 avoids frequent small top-ups.
3. Poll to confirm.
4. Report: "Your balance was €Y, I topped up €Z, it's now €W."

### Pre-Spend Check (No Purchase)

User asks "do I have enough for €X?":

1. Check balance.
2. If balance ≥ X → "You have €Y, that covers it."
3. If balance < X → "You're short by €Z. Want me to top up?"

### One-Time Top-Up

User says "top up €50":

1. Top up with `"50.00"`.
2. Poll to confirm.
3. Report old and new balance.

---

## Step 5 — Card Data Security Rules

These rules apply everywhere. Non-negotiable.

1. **Fetch on demand only.** Call `GET /card-data` only when actively filling a checkout form. Never prefetch.
2. **Never log.** Do not write card number, CVV, or full response to logs, console, files, or any output.
3. **Never display in full.** If the user asks "what's my card number?", show only the last 4 digits. They can see the full details in the Holyheld app.
4. **Never store.** Do not save card data to disk, environment variables, or any persistent storage. Hold in memory only for the duration of the checkout.
5. **Never transmit unnecessarily.** Card data goes into the payment form and nowhere else.
6. **MCP: keep the server local.** Card data should never leave the user's machine.

---

## Common Issues

| Issue | Cause | Fix |
|---|---|---|
| Token is `__HOLYHELD_AGENT_TOKEN__` | Generic skill, not personalized | User must get their link from the Holyheld app (Manage cards → AI card → Copy instructions) and paste it so the agent can install |
| User isn't a Holyheld customer yet | No account | Walk through Step 0 onboarding via holyheld.com/$holyheldagent |
| User pasted a link but token is still placeholder | Link expired (>24H old or new key generated) | Ask user to get a fresh link from the Holyheld app |
| `403` on every request | Token revoked or invalid | User must get a new link from the app → agent re-installs the updated skill |
| Environment check returns `NETWORK_BLOCKED` | Sandboxed runtime | Use MCP Mode (Step 2B) |
| `401` on every request | Missing Authorization header | Add `Authorization: Bearer <token>` header |
| Top-up `200` but balance unchanged | Settlement in progress | Poll up to 5 minutes |
| `AI_TOPUP_INSUFFICIENT_BALANCE` | Insufficient funds in Holyheld account | User must add funds to their Holyheld account |
| `AI_TOPUP_LIMIT_EXCEEDED` | Cumulative limit reached | User must adjust spending limit in the Holyheld app |
| `WRONG_REQUEST` on top-up | Amount is number type or >2 decimals | Send as string: `"50.00"` |
| MCP tools don't appear | Config error or not restarted | Check JSON, absolute path, full restart |
| `node: command not found` | Node.js not installed | Install from https://nodejs.org (v18+) |
