URL: /code/integrations/calls/authorize · Id: `page:/code/integrations/calls/authorize`

# Mandate.authorize

> Let an agent act as you inside one Limit, until a date you set.

`POST https://api.sb/{your-domain}/mandates/authorize`

Let an agent act as you inside one [Limit](https://startupbuilder.dev/business/nouns/limit), until a date you set.

- **Action class:** None
- **Approval:** Runs on its own
- **Emits:** `Mandate.authorized`
- **Price:** Free

```text title="Run it · Ask"
Let my paid media agent open the LinkedIn test without asking me, up to $100 of spend, until November 30.
```

```ts title="Run it · SDK"
import { $ } from 'startup-builder'

const result = await $.try.Mandate.authorize({ agent: 'agent_Pm3xT7rQ1z', verb: 'Campaign.create', limit: { amount: { amount: 100, currency: 'USD' } }, for: 'dana@leaseline.app', expiresAt: '2026-11-30T23:59:59Z' })
```

```json title="Run it · Response"
{
  "api": "https://api.sb/leaseline.app",
  "mandate": {
    "$type": "Mandate",
    "agent": "https://api.sb/leaseline.app/agents/agent_Pm3xT7rQ1z",
    "verb": "Campaign.create",
    "limit": {
      "amount": {
        "amount": 100,
        "currency": "USD"
      }
    },
    "for": "Dana Okafor",
    "topics": [],
    "channels": [],
    "status": "active",
    "expiresAt": "2026-11-30T23:59:59Z"
  },
  "action": {
    "operation": "Mandate.authorize",
    "mode": "try",
    "status": "done"
  }
}
```

Gives your agent a standing approval: one Verb, inside one Limit, until the date you set. [The hub’s definition of `Mandate.authorize`](https://startupbuilder.dev/business/nouns/mandate/authorize).</HubDefinition>

## Inputs
| Name | Type | Default | Meaning |
| --- | --- | --- | --- |
| `agent` | `Agent` | Required | The agent that receives your standing approval. |
| `verb` | `string` | Required | The Verb it covers, as `Noun.verb`. |
| `limit` | `Limit` | Required | The bound your standing approval holds to. |
| `for` | `Party` | `null` | The person the agent acts for, under delegation: the principal and author of record. Unset: the caller is the principal. |
| `channels` | `Channel[]` | `[]` | The Channels it may send through. Empty: the Channels the Agent’s Permissions already allow. |
| `topics` | `string[]` | `[]` | What it may send about; a send outside them becomes an Approval. Empty: no send becomes an Approval for its topic. |
| `approve` | `json` | `null` | The approval rule for one-way sends, such as the first 20 waiting for an Approval. Unset: the approval rule by action class on the hub’s approvals page applies. |
| `window` | `string` | `null` | The delay window each one-way send waits, as a duration. Unset: each Verb’s own delay window. |
| `expiresAt` | `datetime` | `now + 60d` | When your standing approval lapses. Unset: 60 days after it is authorized. |

## Outputs
| Field | Type | What `null` means |
| --- | --- | --- |
| `mandate` | `Mandate` | Never null. |
| `mandate.status` | `string` | Never null: `active` once authorized; in try mode, the status it would take. |
| `mandate.limit` | `Limit` | Never null: the Limit every Action under it is checked against before it runs. |
| `mandate.expiresAt` | `datetime` | Never null: a standing approval always lapses, at the date sent or 60 days out. |

## Effects
**Action class**

None: no approval rule names it, so it runs within its seat’s Permissions and Limits

**Modes**

- `try`: Checks the Limit against the authorizer’s own Permissions and returns your standing approval as it would stand; nothing is granted.
- `send`: Returns a pending Action while its delay window runs; the Agent acts under your standing approval once `Mandate.authorized` fires.
- `do`: Holds the call for the Mode’s hold time, then returns `202` with the pending Action if the window is still open.

**Idempotency**

Only with a key: the same `Idempotency-Key` and input return the first Action, and the same key with other input returns `409`. A repeat without a key grants a second standing approval; every Action names the one it ran under.

**Saved or shared**

The standing approval keeps its principal, so every Action under it records whom it acted for. `Mandate.revoke` ends it at once.

**Emits**

`Mandate.authorized` with `{ mandate, agent, verb, limit, expiresAt }`

**Performed by**

[Treasurer](https://startupbuilder.dev/agents/finance/treasurer)

**Price**

Free on your own account: the platform charges nothing for the call.

## On each Venue
None.

## What try does
None.

## Refuses when
This call has no refusals of its own. Every call through an account can also meet [the shared account refusals](/code/refusals#accounts): [`grant-missing`](/code/refusals#grant-missing), [`needs-reauth`](/code/refusals#needs-reauth), [`account-not-connected`](/code/refusals#account-not-connected), [`over-mandate`](/code/refusals#over-mandate), [`quota-reached`](/code/refusals#quota-reached), [`venue-rejected`](/code/refusals#venue-rejected).

## When it waits
| Fact | Value |
| --- | --- |
| `send` returns | A pending Action, `reason: 'awaitingTime'`, while its delay window runs. |
| Delivered by | `Mandate.authorized` |

## Example

**Ask**

```text
Let my paid media agent open the LinkedIn test without asking me, up to
$100 of spend, until November 30.
```

**API**

```bash
curl -X POST "https://api.sb/leaseline.app/mandates/authorize?mode=try" \
  -H "Authorization: Bearer $SB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"agent":"agent_Pm3xT7rQ1z","verb":"Campaign.create","limit":{"amount":{"amount":100,"currency":"USD"}},"for":"dana@leaseline.app","expiresAt":"2026-11-30T23:59:59Z"}'
```

The response is the one in the Run it window above.

**CLI**

```bash
npx startup-builder do Mandate.authorize --try --data '{"agent":"agent_Pm3xT7rQ1z","verb":"Campaign.create","limit":{"amount":{"amount":100,"currency":"USD"}},"for":"dana@leaseline.app","expiresAt":"2026-11-30T23:59:59Z"}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "mode": "try",
    "code": "return await $.Mandate.authorize({ agent: 'agent_Pm3xT7rQ1z', verb: 'Campaign.create', limit: { amount: { amount: 100, currency: 'USD' } }, for: 'dana@leaseline.app', expiresAt: '2026-11-30T23:59:59Z' })"
  }
}
```

**SDK**

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

const result = await $.try.Mandate.authorize({
  agent: 'agent_Pm3xT7rQ1z',
  verb: 'Campaign.create',
  limit: { amount: { amount: 100, currency: 'USD' } },
  for: 'dana@leaseline.app',
  expiresAt: '2026-11-30T23:59:59Z',
})
```

To retry, send the same `Idempotency-Key`: the retry returns the first answer for $0. A new key reads again.

A `sk_test_` key answers these the same way every time, at $0 ([every test value](/code/data#test-values)).

| Test value | Always returns | What it is for |
| --- | --- | --- |
| `limit: { amount: { amount: 1000000, currency: 'USD' } }` | `{"status":"active","limit":{"amount":{"amount":5000,"currency":"USD"}}}` | A test authorizer holds a $5,000 spend Permission, so a wider Limit comes back narrowed to it: a standing approval never widens a Permission. |
| `maxCost: 0.0001` | Refuses with [`max-cost`](/code/refusals#max-cost) | Any priced call with a maxCost of 0.0001 is refused before it spends: the price is above the cap. |
| `rate-limited.test` | Refuses with [`rate-limited`](/code/refusals#rate-limited) | Any call naming this hostname answers 429 with Retry-After, so your retry logic runs. |
| `sources-busy.test` | Refuses with [`sources-busy`](/code/refusals#sources-busy) | Any call naming this hostname answers 503 as if every route were busy, and nothing is spent. |

## Used by
- **Workflows:** [Ad test setup](/capabilities/run-ads/ad-test-setup)
- **Jobs:** [Earn first links](/capabilities/earn-first-links) · [Publish and post](/capabilities/publish-and-post) · [Reach out](/capabilities/reach-out) · [Run ads](/capabilities/run-ads)

## Related
[`Integration.connect`](/code/integrations/calls/connect) · [`Integration.call`](/code/integrations/calls/call) · [`Handle.connect`](/code/integrations/calls/connect-handle)