You can use Orb to both manage prepaid credit balances for your customers and to control access to your product based on those balances. This is useful for companies that want to ensure that customers are only able to access their product if they’ve paid for it in advance.

In this guide, we’ll walk through how to integrate Orb’s prepaid credit management functionality and alerting features to accurately manage product access. We’ll use a model that’s a very simple version of a text message API that uses credits:

  • No messages are sent unless the customer has purchased credits.
  • Customers must purchase credits through your product’s admin dashboard. Each credit costs $0.01.
  • The customer can purchase credits in advance, and the credits will be automatically deducted from their balance as messages are sent.
  • Messages can be used at very high throughput, and the customer needs to be blocked from sending messages if they run out of credits as close to the event as possible.
  • Sending messages is performance critical, so it’s not possible to incur the latency of checking the customer’s current balance before each message send takes place. Incurring this latency is acceptable on a very infrequent basis.

Prerequisites

In your integration, ensure that you create an Orb customer corresponding to each of your own customers. A customer always starts with 0 credits in each of their prepaid balances; for the purposes of this example, we’ll work in a custom prepaid credits currency called message_credits.

Upon creating a customer via the create customer API endpoint:

  • Create a balance depleted and balance recovered alert via the API. Ensure that you’re creating these alerts in the correct currency, passing the identifier of the currency (in this case message_credits). For these alert types, the thresholds body parameter is not required, since the threshold is implicitly set to 0 and these alerts fire on either side of that threshold. As long as these alert configurations are created, you won’t need to store the configuration ID in your data model.
  • Create a subscription for the customer. Note that subscriptions in Orb do not have to be explicitly marked as prepaid, and the customer does not have to be specially configured to draw down from the prepaid balance. Instead, any price that’s part of the subscription that is in the message_credits currency will automatically draw down from the message_credits prepaid balance. You can mix and match price currencies in a subscription, and Orb will automatically draw down from the appropriate balance.
  • In your data model, create a nullable timestamp field called messages_blocked_until. When this field is a non-null timestamp in the future, the customer is blocked from sending messages. When this field is null, the customer is not blocked from sending messages.

Managing new credit purchases

In order to support credit purchasing through your dashboard, your server-side logic should increment credits using the create ledger entry endpoint, passing in the following parameters:

  • Set the currency to message_credits.
  • entry_type = increment signifies an increment
  • amount represents the additional credits being bought.
  • per_unit_cost_basis represents the cost of each token in your invoicing currency, USD. Set this to $0.01, since each credit costs $0.01.
  • expiry_date represents when the message_credit will automatically be depleted. Set this to one year from the current grant date.
  • To ensure credits are billed immediately and granted conditional on payment success, set invoice_settings.auto_collection to True, invoice_settings.require_successful_payment to True, and invoice_settings.net_termsto 0.

Sending messages

In your application logic, you need to ensure that customers are only able to send messages if they have enough credits to do so by interpreting the messages_blocked_until field. If this field is null or a future timestamp, your application should allow the customer to send messages.

Use the following logic to determine eligibility based on the value of messages_blocked_until:

  • If the field is null, your application should allow the customer to send messages. This is the case where the customer has a positive credit balance.
  • If this field is MAX_DATE, your application should block the customer from sending messages. This is the case where the customer has no known credit blocks that may become effective.
  • If the field is a future timestamp, your application should disallow the customer from sending messages. This is the case where the customer has no positive credit balance, but there is a credit block that will become effective in the future.
  • If the field is a non-0 timestamp in the past, your application should re-query the customer’s current balance using the current credit balance and make a determination based on the new value of messages_blocked_until. This is the case where the customer had no positive credit balance, but a credit block may have become effective so a re-query is required.

Note that this check must be performed on every message send, since the customer may have purchased more credits in between messages, the customer may have exhausted their credits, or the passage of time may have caused a credit block to expire or become effective.

Blocking product access on depletion

Orb guarantees the delivery of a balance depleted alert when there are 0 remaining message_credits. When testing, parse this webhook and confirm that properties.pricing_unit.name matches message_credit.

Simply listening to this webhook is insufficient. This is because webhook systems do not provide ordering guarantees, and you need to ensure that the customer is only blocked from sending the message under the appropriate conditions.

For example, suppose that a customer has a balance of 10 message credits, sends 10 messages and immediately purchases a new block in quick succession. In this case, the balance depleted alert may be delivered via webhook after the block has been purchased, and the customer will be erroneously blocked from sending messages.

Instead, you should always re-query the customer’s current credit balance using the API when you receive a balance depleted webhook.

This API is never cached, and will always return the most up to date balance per the usage events that Orb has ingested; it will also reflect any other operations that have been performed on the balance, such as the purchase of new credits or the expiration of existing credits. For this request, be sure to set include_all_blocks=True to only query for active blocks at the current point in time.

In order to set messages_blocked_until, use the following logic:

  • If the balance is greater than 0, set messages_blocked_until to null.
  • If the balance is 0, set messages_blocked_until to the earliest future effective_date of any of the customer’s credit blocks. If the customer has no credit blocks effective in the future, set messages_blocked_until to MAX_DATE.

Unblocking product access

If your customer purchases more credits through your product dashboard, continue using the create ledger entry endpoint as detailed above. It’s also important to ensure that new credits are set to be effective as of the beginning of the current subscription billing period. You can accomplish this by setting the effective_date of the entry to match the current_billing_period_start_date of the subscription (you’ll need to make sure that you have the latest subscription response).

This amounts to ‘backdating’ the credits to the beginning of the billing period, and ensures that your customer never incurs any overages for the current billing period. Specifically, if there are seconds or minutes of lag between the consumption of credits and your system consuming the webhook, your customer may temporarily accrue a small overage charge until product access is blocked (and more credits are bought). When the customer purchases more credits, the above effective_date strategy will ensure no charges remain on the current period invoice.

After creating the block, query again for the customer’s current balance using the current credit balance endpoint and use the same exact logic detailed above, setting messages_blocked_until to null, MAX_DATE, or a different future effective date.

Handling balance recovered webhooks

Orb will send a balance recovered webhook when a customer’s credit balance is no longer at 0. This may happen when a customer purchases more credits, and handles the case where this action happened outside of the context above (e.g. through a manual intervention by a customer service representative). This will also handle cases like a subscription change where a user upgrades to a subscription that includes a recurring credit allocation, because Orb will automatically create a new credit block and send a “balance recovered” webhook. Similar to the balance depleted webhook, this webhook will not provide ordering guarantees so it’s important to not solely depend on this webhook to unblock product access.

When you receive this webhook, you should re-query the customer’s current balance using the current credit balance. Follow the same logic as above to determine the value of messages_blocked_until.

Automated credit allocations

Orb supports the concept of a recurring allocation, which is the concept of a credit block that is automatically granted by the subscription on a recurring basis. This is useful for companies that want to provide their customers with a certain amount of credits each month as part of their subscription.

When a customer has an active allocation, future credit blocks are created automatically by the system, and Orb guarantees that the next block will exist while the current one is active. Because of this, the above strategy of setting messages_blocked_until to the earliest future effective date will ensure that future allocations are accounted for in our message sending strategy.

Credit expirations

In Orb, each credit block can expire at any time after it is effective. When a credit block expires, you will receive a balance depleted alert. As per the section above, you should re-query the customer’s current balance using the current credit balance API and update messages_blocked_until accordingly. If the customer has no remaining blocks after the expiration, messages_blocked_until will be set to MAX_DATE.

Overages due to eventual consistency

Because Orb’s event ingestion system is designed to be eventually consistent and it’s not feasible to wait for all events to be ingested before sending a message, it’s still possible that a customer may send a very small number of messages before they’re blocked after they’ve exhausted all their message credits. We recommend setting your conversion_rate to $0.00 for the message_credits currency, since this will ensure that the customer is not charged for any overages that may occur. In the vast majority of cases, this is an acceptable tradeoff but reach out to Orb if you want to discuss how faster SLAs may bound the potential overage costs.