# Issue a card with a limit

URL: /systems/finance/issue-a-card-with-a-limit · Id: `guide:issue-a-card-with-a-limit`

> Say "give my ads agent a card with a $500 limit" and get a spending rule, a virtual card, approval over the limit and every charge in your books.

Give an [Agent](/business/nouns/agent) or a [Human](/business/nouns/human) a [Card](/business/nouns/card) that spends only on what you allow, up to a [Limit](/business/nouns/limit). The rule it spends under is a [Mandate](/business/nouns/mandate).

Needs: [Open a bank account](/systems/finance/open-a-bank-account) · [Agent](/business/nouns/agent)

**Before real money moves**

- Who says yes: You. Your agent shows you the run and waits for your [Approval](/business/nouns/approval).
- You leave the chat: Never: it all happens in the chat.
- First question at Idea: “Which merchant may it pay?”

## What you get
- 1 spending rule naming who may spend and on what (Mandate)
- 1 limit per month, per charge and per merchant (Limit)
- 1 virtual card with those spending controls (Card)
- 1 cardholder record for your company (Company)
- 1 approval request to you for any charge over the limit (Approval)
- 2 lines in your books per charge: the cost and the cash out (Entry)

## Your agent does
**For agents**

- Guide: `guide:issue-a-card-with-a-limit`, status `beta`. Page: [/systems/finance/issue-a-card-with-a-limit.md](/systems/finance/issue-a-card-with-a-limit.md).
- Approval: ask the founder first. Show the run, then wait for their Approval; a refusal ends it.
- Leaves the chat: never.
- Needs first: [Open a bank account](/systems/finance/open-a-bank-account), [Agent](/business/nouns/agent).
- Run: call the steps in order, each through the API, SDK, CLI or MCP tab below; a later step passes the `$id` an earlier one returns. Each value marked example is the sample business’s: pass the founder’s own.

| Step | Verb | Inputs (type, required, this guide’s value) | Returns |
| --- | --- | --- | --- |
| 1 | [`Mandate.authorize`](/business/nouns/mandate/authorize) (`verb:Mandate.authorize`) | `agent` Agent (an id), required: example `"agent_ads"`; `verb` string, required: example `"Mandate.spend"`; `limit` Limit (an id), required: example `"limit_ads_500_monthly"` | `mandate` Mandate |
| 2 | [`Card.issue`](/business/nouns/card/issue) (`verb:Card.issue`) | `mandate` Mandate (an id), required: the `$id` step 1 returns; `form` string, required: example `"merchant"`; `merchant` string: example `"Google Ads"` | `card` Card |

Returns: the last call answers with `card` and its `action`, whose `status` is `done`, `pending` (waiting on an Approval or a Worker), `failed`, `canceled` or `refused`:

```json
{
  "api": "https://api.sb/leaseline.app",
  "card": {
    "$id": "https://api.sb/leaseline.app/cards/card_1",
    "$type": "Card",
    "form": "merchant",
    "status": "active"
  },
  "action": {
    "$id": "https://api.sb/leaseline.app/actions/action_1",
    "operation": "Card.issue",
    "status": "done"
  },
  "links": {
    "self": "https://api.sb/leaseline.app/actions/action_1"
  }
}
```

Next: offer “Raise the limit to $800 for March”, or “Freeze the card”.

Calls: `Mandate.authorize` → `Card.issue`

Ask:

```text
Give my ads agent a card with a $500 monthly limit.
Get me a virtual card for software, $200 a month at most.
Let the growth agent spend up to $500 a month on ads.
```

API:

```bash
# 1. Mandate.authorize
MANDATE=$(curl -X POST https://api.sb/leaseline.app/mandates/authorize \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"agent":"agent_ads","verb":"Mandate.spend","limit":"limit_ads_500_monthly"}' | jq -r '.mandate["$id"] | split("/") | last')

# 2. Card.issue
curl -X POST https://api.sb/leaseline.app/cards/issue \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"mandate":"'"$MANDATE"'","form":"merchant","merchant":"Google Ads"}'
```

SDK:

```ts
import { $ } from 'startup-builder'

const mandate = await $.Mandate.authorize({ agent: 'agent_ads', verb: 'Mandate.spend', limit: 'limit_ads_500_monthly' })
await $.Card.issue({ mandate: mandate.$id, form: 'merchant', merchant: 'Google Ads' })
```

CLI:

```bash
MANDATE=$(npx startup-builder do Mandate.authorize --json '{"agent":"agent_ads","verb":"Mandate.spend","limit":"limit_ads_500_monthly"}' | jq -r '.mandate["$id"] | split("/") | last')
npx startup-builder do Card.issue --json '{"mandate":"'"$MANDATE"'","form":"merchant","merchant":"Google Ads"}'
```

MCP:

```json
{
  "name": "do",
  "arguments": {
    "code": "const mandate = await $.Mandate.authorize({ agent: 'agent_ads', verb: 'Mandate.spend', limit: 'limit_ads_500_monthly' })\nreturn await $.Card.issue({ mandate: mandate.$id, form: 'merchant', merchant: 'Google Ads' })"
  }
}
```

## Change it
- Raise the limit to $800 for March.
- Freeze the card.
- Add a second card for Meta ads.

## Under the hood
- Systems: [Spend management](/systems/spend), [Treasury](/systems/treasury), [Ledger](/systems/ledger), [Work queue](/agents/work-queue)
- Nouns: [Mandate](/business/nouns/mandate), [Limit](/business/nouns/limit), [Card](/business/nouns/card), [Approval](/business/nouns/approval), [Entry](/business/nouns/entry)
- Events: [`Mandate.authorized`](/business/events/Mandate.authorized), [`Card.issued`](/business/events/Card.issued)
- Stripe objects: `Issuing Cardholder`, `Issuing Card`, `Issuing Authorization`, `Issuing Transaction`

A Card spends only from the card balance. Stripe asks the platform to authorize each charge, and a charge outside the Mandate is declined at the terminal. A charge over the Limit waits for an [Approval](/business/nouns/approval) from the [Role](/business/nouns/role) that holds the Mandate.