# Offer routes: accept, grant, publish, subscribe

URL: /code/api/offer/verbs · Id: `page:code/api/offer/verbs`

> The Offer Verbs accept, grant, publish, subscribe as api.sb routes, with inputs, result, refusals and an example.

Reference for [Offer](/business/nouns/offer), one group of [Offer routes](/code/api/offer).

### Offer.accept
Accept records that a party takes an Offer, which makes that party a Customer of the Startup. [Verb page](/business/nouns/offer/accept?interface=api).

```text
POST /{namespace}/offers/{offer}/accept
```

Path: `offer`. Body, as JSON: the other inputs. Emits [`Offer.accepted`](/business/events/Offer.accepted).

**Inputs**

| Input | Type | Required | Description |
| --- | --- | --- | --- |
| `offer` | [Offer](/business/nouns/offer) | Yes | The Offer to accept. |
| `party` | `string` | Yes | The person or organization accepting. |

**Returns**

| Output | Type | Required | Description |
| --- | --- | --- | --- |
| `customer` | [Customer](/business/nouns/customer) | Yes | The Customer the party is or becomes. |
| `offer` | [Offer](/business/nouns/offer) | Yes | The Offer accepted. |

**Refuses when**

| Check | Error | Fix | Then call |
| --- | --- | --- | --- |
| The operation is part of an Offer the caller has not accepted or paid for. | [`payment-required`](/code/api/errors/payment-required) | Accept the Offer named in the response, then retry the call. | None. |
| The key is valid, but its scopes do not allow this operation. | [`forbidden`](/code/api/errors/forbidden) | Use a key whose scopes cover the operation, or ask an owner of the namespace to grant them. | None. |
| The record is not in a state that allows this Verb, so the call is refused. | [`refused`](/code/api/errors/refused) | Read the record, run the Verb that moves it to an allowed state, then retry. | None. |

**Example**

```bash
curl -X POST https://api.sb/leaseline.app/offers/offer_2hzYe1ERo0/accept \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"party":"Pinecrest Homes"}'
```

```json
{
  "api": "https://api.sb/leaseline.app",
  "customer": {
    "$id": "https://api.sb/leaseline.app/customers/cus_Gp5HXPKk00",
    "$type": "Customer",
    "since": "2026-10-22T12:00:00Z"
  },
  "offer": {
    "$id": "https://api.sb/leaseline.app/offers/offer_2hzYe1ERo0",
    "$type": "Offer",
    "price": {
      "amount": 29,
      "currency": "USD"
    }
  },
  "action": {
    "$id": "https://api.sb/leaseline.app/actions/act_br07HsG8MG",
    "operation": "Offer.accept",
    "status": "done"
  },
  "links": {
    "self": "https://api.sb/leaseline.app/actions/act_br07HsG8MG"
  }
}
```

### Offer.grant
Grant adds an Entitlement to an Offer: a Verb its Customers may call, optionally bounded by a Limit. [Verb page](/business/nouns/offer/grant?interface=api).

```text
POST /{namespace}/offers/{offer}/grant
```

Path: `offer`. Body, as JSON: the other inputs. Emits [`Offer.granted`](/business/events/Offer.granted).

**Inputs**

| Input | Type | Required | Description |
| --- | --- | --- | --- |
| `offer` | [Offer](/business/nouns/offer) | Yes | The Offer that grants it. |
| `verb` | [Verb](/business/nouns/verb) | Yes | The Verb its Customers may call, as `Noun.verb`. |
| `limit` | [Limit](/business/nouns/limit) | No | The Limit that bounds it. Unset: a feature, called as often as the Customer likes. |

**Returns**

| Output | Type | Required | Description |
| --- | --- | --- | --- |
| `entitlement` | [Entitlement](/business/nouns/entitlement) | Yes | The new Entitlement, granted by the Offer. |
| `offer` | [Offer](/business/nouns/offer) | Yes | The Offer, with the Entitlement added. |

**Refuses when**

| Check | Error | Fix | Then call |
| --- | --- | --- | --- |
| The key is valid, but its scopes do not allow this operation. | [`forbidden`](/code/api/errors/forbidden) | Use a key whose scopes cover the operation, or ask an owner of the namespace to grant them. | None. |

**Example**

```bash
curl -X POST https://api.sb/leaseline.app/offers/offer_2hzYe1ERo0/grant \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"verb":"verb_hlkWBvrAyZ"}'
```

```json
{
  "api": "https://api.sb/leaseline.app",
  "entitlement": {
    "$id": "https://api.sb/leaseline.app/entitlements/ent_7MQACmrzGj",
    "$type": "Entitlement",
    "verb": "verb_hlkWBvrAyZ"
  },
  "offer": {
    "$id": "https://api.sb/leaseline.app/offers/offer_2hzYe1ERo0",
    "$type": "Offer",
    "price": {
      "amount": 29,
      "currency": "USD"
    }
  },
  "action": {
    "$id": "https://api.sb/leaseline.app/actions/act_br07HsG8MG",
    "operation": "Offer.grant",
    "status": "done"
  },
  "links": {
    "self": "https://api.sb/leaseline.app/actions/act_br07HsG8MG"
  }
}
```

### Offer.publish
Publish makes an Offer available for parties to accept. [Verb page](/business/nouns/offer/publish?interface=api).

```text
POST /{namespace}/offers/{offer}/publish
```

Path: `offer`. Emits [`Offer.published`](/business/events/Offer.published).

**Inputs**

| Input | Type | Required | Description |
| --- | --- | --- | --- |
| `offer` | [Offer](/business/nouns/offer) | Yes | The Offer to publish. |

**Returns**

| Output | Type | Required | Description |
| --- | --- | --- | --- |
| `offer` | [Offer](/business/nouns/offer) | Yes | The Offer, now open to accept. |

**Refuses when**

| Check | Error | Fix | Then call |
| --- | --- | --- | --- |
| A Service Offer promises a remedy or a pricing basis that its Service’s checks cannot back. | [`contract-unsupported`](/code/api/errors/contract-unsupported) | Add a stronger check to the Service’s steps, or offer a cheaper remedy or basis, then publish again. | None. |
| The key is valid, but its scopes do not allow this operation. | [`forbidden`](/code/api/errors/forbidden) | Use a key whose scopes cover the operation, or ask an owner of the namespace to grant them. | None. |
| The record is not in a state that allows this Verb, so the call is refused. | [`refused`](/code/api/errors/refused) | Read the record, run the Verb that moves it to an allowed state, then retry. | None. |
| The median Customer’s Cost leaves less than the Startup’s target margin at this Price. | [`below-contribution-floor`](/code/api/errors/below-contribution-floor) | Raise the Price to the floor the error names, or grant fewer Entitlements. | [Offer.update](/business/nouns/offer/update) |

**Example**

```bash
curl -X POST https://api.sb/leaseline.app/offers/offer_2hzYe1ERo0/publish \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
```

```json
{
  "api": "https://api.sb/leaseline.app",
  "offer": {
    "$id": "https://api.sb/leaseline.app/offers/offer_2hzYe1ERo0",
    "$type": "Offer",
    "price": {
      "amount": 29,
      "currency": "USD"
    }
  },
  "action": {
    "$id": "https://api.sb/leaseline.app/actions/act_br07HsG8MG",
    "operation": "Offer.publish",
    "status": "done"
  },
  "links": {
    "self": "https://api.sb/leaseline.app/actions/act_br07HsG8MG"
  }
}
```

### Offer.subscribe
Subscribe accepts a recurring Offer as a Subscription that renews each period until it ends. [Verb page](/business/nouns/offer/subscribe?interface=api).

```text
POST /{namespace}/offers/{offer}/subscribe
```

Path: `offer`. Body, as JSON: the other inputs. Emits [`Offer.subscribed`](/business/events/Offer.subscribed).

**Inputs**

| Input | Type | Required | Description |
| --- | --- | --- | --- |
| `offer` | [Offer](/business/nouns/offer) | Yes | The recurring Offer. |
| `quantity` | `integer` | No | Seats or units. Defaults to 1. |
| `promotionCode` | `string` | No | A promotion code the buyer typed; applies its Coupon for the Coupon’s duration. |

**Returns**

| Output | Type | Required | Description |
| --- | --- | --- | --- |
| `subscription` | [Subscription](/business/nouns/subscription) | Yes | The new Subscription. |
| `offer` | [Offer](/business/nouns/offer) | Yes | The Offer subscribed to. |

**Refuses when**

| Check | Error | Fix | Then call |
| --- | --- | --- | --- |
| The input does not match the schema of the operation. | [`invalid-input`](/code/api/errors/invalid-input) | Read the schema of the operation, then send every required field with the right type. | None. |
| The operation is part of an Offer the caller has not accepted or paid for. | [`payment-required`](/code/api/errors/payment-required) | Accept the Offer named in the response, then retry the call. | [Offer.accept](/business/nouns/offer/accept) |
| The key is valid, but its scopes do not allow this operation. | [`forbidden`](/code/api/errors/forbidden) | Use a key whose scopes cover the operation, or ask an owner of the namespace to grant them. | None. |
| The record is not in a state that allows this Verb, so the call is refused. | [`refused`](/code/api/errors/refused) | Read the record, run the Verb that moves it to an allowed state, then retry. | None. |

**Example**

```bash
curl -X POST https://api.sb/leaseline.app/offers/offer_2hzYe1ERo0/subscribe \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
```

```json
{
  "api": "https://api.sb/leaseline.app",
  "subscription": {
    "$id": "https://api.sb/leaseline.app/subscriptions/sub_jpvy3zejKB",
    "$type": "Subscription",
    "status": "active"
  },
  "offer": {
    "$id": "https://api.sb/leaseline.app/offers/offer_2hzYe1ERo0",
    "$type": "Offer",
    "price": {
      "amount": 29,
      "currency": "USD"
    }
  },
  "action": {
    "$id": "https://api.sb/leaseline.app/actions/act_br07HsG8MG",
    "operation": "Offer.subscribe",
    "status": "done"
  },
  "links": {
    "self": "https://api.sb/leaseline.app/actions/act_br07HsG8MG"
  }
}
```