Skip to content
ABUNA
DocsPrices

API reference

Prices

Set how much a product costs and how often it renews.

A price sets how much a product costs and how often it renews. A product can have several prices, like a monthly and a yearly one.

The price object

Attributes

  • idstring

    Unique identifier for the price.
  • product_idstring

    The product the price belongs to.
  • namestring

    The price's name, like Monthly.
  • currencystring

    XAF or XOF.
  • amountinteger

    What the customer pays each billing period, in the currency's smallest unit.
  • intervalstring

    The unit of a billing period: day, week, month, or year.
  • interval_countinteger

    How many intervals make up one billing period. month with 3 bills every three months.
  • created_attimestamp

    When the price was created, in Unix seconds.
  • archived_attimestampnullable

    When the price was archived. An archived price starts no new subscriptions.
  • pending_changeobjectnullable

    The amount change scheduled for the price, or null when none is. A price has at most one.
    Show child attributesHide child attributes
    • idstring

      Unique identifier for the change. Price change events carry it as price_change_id.
    • amountinteger

      The new amount, in the currency's smallest unit.
    • previous_amountinteger

      The amount before the change.
    • currencystring

      The price's currency, XAF or XOF.
    • effective_attimestamp

      The start of the day the new amount applies from, in your app's time zone.
    • keep_current_subscribersboolean

      true when everyone subscribed on that day keeps the old amount.
    • created_attimestamp

      When the change was scheduled.
The price object
{
  "id": "01J9ZQ5N1P2Q3R4S5T6V7W8XYZ",
  "product_id": "01J9ZQ5A2B3C4D5E6F7G8H9JKM",
  "name": "Monthly",
  "currency": "XAF",
  "amount": 5000,
  "interval": "month",
  "interval_count": 1,
  "created_at": 1727600000,
  "archived_at": null,
  "pending_change": null
}

Create a price

POST/v1/apps/{appID}/prices

Creates a price for one of the app's products.

Parameters

  • product_idstringrequired

    The product to price.
  • namestringrequired

    The price's name, like Monthly.
  • currencystringrequired

    XAF or XOF. Lowercase works too.
  • amountintegerrequired

    What the customer pays each billing period, in the currency's smallest unit. Must be greater than 0.
  • intervalstringrequired

    day, week, month, or year.
  • interval_countinteger

    How many intervals make up one billing period. Defaults to 1. At most 365 days, 52 weeks, 12 months, or 1 year.

Returns the price with status 201. An invalid field returns 422 with code invalid. If the app has no product with that product_id, returns 404 with code not_found.

POST /v1/apps/{appID}/prices
curl https://api.abuna.app/v1/apps/01J9ZQ4Y7R3T6V8W2X5B1C0DEF/prices \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"product_id": "01J9ZQ5A2B3C4D5E6F7G8H9JKM", "name": "Monthly", "currency": "XAF", "amount": 5000, "interval": "month"}'
Response
{
  "id": "01J9ZQ5N1P2Q3R4S5T6V7W8XYZ",
  "product_id": "01J9ZQ5A2B3C4D5E6F7G8H9JKM",
  "name": "Monthly",
  "currency": "XAF",
  "amount": 5000,
  "interval": "month",
  "interval_count": 1,
  "created_at": 1727600000,
  "archived_at": null,
  "pending_change": null
}

List prices

GET/v1/apps/{appID}/prices

Lists every price in the app, across all products and including archived ones, newest first.

Returns an array of price objects. It isn't paginated.

GET /v1/apps/{appID}/prices
curl https://api.abuna.app/v1/apps/01J9ZQ4Y7R3T6V8W2X5B1C0DEF/prices \
  -H "Authorization: Bearer sk_test_..."
Response
[
  {
    "id": "01J9ZQ5N1P2Q3R4S5T6V7W8XYZ",
    "product_id": "01J9ZQ5A2B3C4D5E6F7G8H9JKM",
    "name": "Monthly",
    "currency": "XAF",
    "amount": 5000,
    "interval": "month",
    "interval_count": 1,
    "created_at": 1727600000,
    "archived_at": null,
    "pending_change": null
  }
]

Update a price

PATCH/v1/apps/{appID}/prices/{id}

Renames the price. Its amount, currency, billing period, and checkout link stay the same. To change what customers pay, schedule a price change.

Parameters

  • namestringrequired

    The new name, like Monthly plan.

Returns the price. A blank name returns 422 with code invalid. If the app has no price with that ID, returns 404 with code not_found.

PATCH /v1/apps/{appID}/prices/{id}
curl https://api.abuna.app/v1/apps/01J9ZQ4Y7R3T6V8W2X5B1C0DEF/prices/01J9ZQ5N1P2Q3R4S5T6V7W8XYZ \
  -X PATCH \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"name": "Monthly plan"}'
Response
{
  "id": "01J9ZQ5N1P2Q3R4S5T6V7W8XYZ",
  "product_id": "01J9ZQ5A2B3C4D5E6F7G8H9JKM",
  "name": "Monthly plan",
  "currency": "XAF",
  "amount": 5000,
  "interval": "month",
  "interval_count": 1,
  "created_at": 1727600000,
  "archived_at": null,
  "pending_change": null
}

Archive a price

POST/v1/apps/{appID}/prices/{id}/archive

Archives the price. It starts no new subscriptions, and subscriptions already on it keep renewing. Archiving an archived price changes nothing. To undo it, unarchive the price.

Returns the price. If the app has no price with that ID, returns 404 with code not_found.

POST /v1/apps/{appID}/prices/{id}/archive
curl https://api.abuna.app/v1/apps/01J9ZQ4Y7R3T6V8W2X5B1C0DEF/prices/01J9ZQ5N1P2Q3R4S5T6V7W8XYZ/archive \
  -X POST \
  -H "Authorization: Bearer sk_test_..."
Response
{
  "id": "01J9ZQ5N1P2Q3R4S5T6V7W8XYZ",
  "product_id": "01J9ZQ5A2B3C4D5E6F7G8H9JKM",
  "name": "Monthly",
  "currency": "XAF",
  "amount": 5000,
  "interval": "month",
  "interval_count": 1,
  "created_at": 1727600000,
  "archived_at": 1727686400,
  "pending_change": null
}

Unarchive a price

POST/v1/apps/{appID}/prices/{id}/unarchive

Unarchives the price. It can start new subscriptions again, and its checkout link works again. Unarchiving a price that isn't archived changes nothing.

Returns the price. If the app has no price with that ID, returns 404 with code not_found.

POST /v1/apps/{appID}/prices/{id}/unarchive
curl https://api.abuna.app/v1/apps/01J9ZQ4Y7R3T6V8W2X5B1C0DEF/prices/01J9ZQ5N1P2Q3R4S5T6V7W8XYZ/unarchive \
  -X POST \
  -H "Authorization: Bearer sk_test_..."
Response
{
  "id": "01J9ZQ5N1P2Q3R4S5T6V7W8XYZ",
  "product_id": "01J9ZQ5A2B3C4D5E6F7G8H9JKM",
  "name": "Monthly",
  "currency": "XAF",
  "amount": 5000,
  "interval": "month",
  "interval_count": 1,
  "created_at": 1727600000,
  "archived_at": null,
  "pending_change": null
}

Schedule a price change

POST/v1/apps/{appID}/prices/{id}/change

Schedules a new amount for the price, starting on a day at least 14 days ahead. The amount never changes right away. The currency and billing period can't change. For a different one, create a new price.

From the start of that day in your app's time zone, checkouts and new subscriptions pay the new amount. Each current subscriber pays it from their first renewal on or after that day. Abuna emails each of them now with the new amount and the date it first applies to them, and messages them on Telegram if they connected it. Customers who subscribe while the change is pending get the same notice when they subscribe.

With keep_current_subscribers, everyone subscribed on that day keeps paying the old amount for as long as they stay subscribed, and only later subscribers pay the new one. A subscriber who moves to another price and back pays the price's amount at that time.

A price has one pending change. Scheduling another replaces it. If the replacement keeps current subscribers on the old amount, those told about the earlier change get an email saying their price stays. Abuna records a price_change.scheduled event, and price_change.applied when the new amount starts.

Parameters

  • amountintegerrequired

    The new amount, in the currency's smallest unit. Must be greater than 0 and different from the current amount.
  • effective_attimestamprequired

    Any time on the day the new amount starts, in Unix seconds. Abuna moves it to the start of that day in your app's time zone. The day must be at least 14 days after today there.
  • keep_current_subscribersboolean

    Keep everyone subscribed on that day on the old amount. Defaults to false.

Returns the price with pending_change set. A failed check returns 422 with code invalid, and each errors item names the field: amount with invalid or unchanged, or effective_at with required or too_soon. Any other field, like currency or interval, returns 400 with code invalid_json. If the app has no price with that ID, returns 404 with code not_found. Once the pending change reaches its day, it can't be replaced, and the request returns 409 with code change_in_effect.

POST /v1/apps/{appID}/prices/{id}/change
curl https://api.abuna.app/v1/apps/01J9ZQ4Y7R3T6V8W2X5B1C0DEF/prices/01J9ZQ5N1P2Q3R4S5T6V7W8XYZ/change \
  -X POST \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"amount": 6000, "effective_at": 1730458800, "keep_current_subscribers": false}'
Response
{
  "id": "01J9ZQ5N1P2Q3R4S5T6V7W8XYZ",
  "product_id": "01J9ZQ5A2B3C4D5E6F7G8H9JKM",
  "name": "Monthly",
  "currency": "XAF",
  "amount": 5000,
  "interval": "month",
  "interval_count": 1,
  "created_at": 1727600000,
  "archived_at": null,
  "pending_change": {
    "id": "01J9ZQ7C1D2E3F4G5H6J7K8M9N",
    "amount": 6000,
    "previous_amount": 5000,
    "currency": "XAF",
    "effective_at": 1730415600,
    "keep_current_subscribers": false,
    "created_at": 1727700000
  }
}

Cancel a price change

POST/v1/apps/{appID}/prices/{id}/change/cancel

Cancels the price's pending change, so the amount stays as it is. If current subscribers were going to move to the new amount, Abuna emails them that their price stays. Abuna records a price_change.canceled event.

Returns the price with pending_change set to null. If the app has no price with that ID, returns 404 with code not_found. With no pending change, returns 409 with code no_pending_change. Once the change reaches its day, it can't be canceled, and the request returns 409 with code change_in_effect.

POST /v1/apps/{appID}/prices/{id}/change/cancel
curl https://api.abuna.app/v1/apps/01J9ZQ4Y7R3T6V8W2X5B1C0DEF/prices/01J9ZQ5N1P2Q3R4S5T6V7W8XYZ/change/cancel \
  -X POST \
  -H "Authorization: Bearer sk_test_..."
Response
{
  "id": "01J9ZQ5N1P2Q3R4S5T6V7W8XYZ",
  "product_id": "01J9ZQ5A2B3C4D5E6F7G8H9JKM",
  "name": "Monthly",
  "currency": "XAF",
  "amount": 5000,
  "interval": "month",
  "interval_count": 1,
  "created_at": 1727600000,
  "archived_at": null,
  "pending_change": null
}

Next steps