RapidCents Developer Documentation
Recurring Payments & Subscriptions Guide
v1.0 · REST API

Recurring Payments

Subscriptions & installments · Automated billing · Card on file · Retry logic

Summary

Recurring Payments (Subscriptions) allow merchants to set up automated billing cycles for customers. Merchants can create open-ended subscriptions or fixed installment plans with configurable frequencies ranging from weekly to annual. The card can be entered by the merchant or by the customer via a hosted payment page.

🔁

RapidCents automatically charges stored cards on the scheduled billing date, sends success/decline notifications to both parties, and retries failed payments on a configurable schedule.

Subscription Lifecycle

  1. Create — Merchant creates a subscription with amount, customer, frequency, start date, and optionally a card. If the merchant enters the card, the subscription is active immediately.
  2. Customer Activation — If the customer enters the card, they receive an email with a link to the hosted page. The customer enters card details and the subscription becomes active.
  3. Scheduled Billing — The billing engine runs daily, identifies subscriptions due for charging, and adds them to the payment queue.
  4. Charge — The payment queue processor charges the stored card. On success, the bill count increments and notifications are sent. On decline, a retry is scheduled.
  5. Pause / Resume — Merchants can pause billing immediately or at period end, optionally preserving unused prepaid time or confirming an external refund. Resume continues the original schedule or restarts billing today.
  6. Completion / Cancellation — Installment plans complete after the specified number of bills. Subscriptions continue until manually cancelled.

Subscription Statuses

INACTIVE (Awaiting Card)

Customer-entered card flow: subscription is created but waiting for the customer to provide card details.

ACTIVE

Subscription is active and will be charged on the next billing date. Card is on file.

PAUSED

Service and automatic billing are stopped. Resume restores prepaid time or starts a new cycle depending on pause behavior.

CANCELLED

Subscription has been cancelled. No further charges will be made.

Processing Flow

Merchant-Entered Card

MerchantCreates & Enters Card
RapidCentsStores Card & Schedules
Billing EngineDaily Scheduler
Visa · Mastercard · Discover · Amex

Customer-Entered Card

MerchantCreates Subscription
RapidCentsSends Email
CustomerEnters Card Details
Billing EngineDaily Scheduler
Visa · MastercardDiscover · Amex

Authentication

Public API v1 is server-to-server. Authenticate with a RapidCents rc_sk_ secret key.

Authorization: Bearer {rc_sk_secret_key}
Content-Type: application/json
Accept: application/json

Base URL pattern: /api/v1/subscriptions. Business is resolved from the secret key and is never accepted from the request body.

Public API v1 — Subscriptions

Server-to-server subscription APIs under /api/v1. Authenticate with a RapidCents rc_sk_ secret key. Raw card PAN/CVC is never accepted; use a stored payment_method or the hosted checkout link returned on create.

💡

Pause and resume on API v1 match the dashboard lifecycle: preview unused prepaid time, pause immediately or at period end, preserve time or confirm an external refund, then resume on the original schedule or restart today.

Subscription responses include top-level pause_at, paused_at, and pause_reason, plus a nullable pause object when an unresolved pause exists:

pause object
{
  "pause": {
    "id": "pause-uuid",
    "status": "pending",
    "unused_time_behavior": "preserve_time",
    "paused_at": 1720000000,
    "paid_through_at": 1721200000,
    "remaining_paid_seconds": 1209600,
    "requires_external_refund_confirmation": false,
    "resume_mode": null
  }
}

API v1 — Create Subscription

POST /api/v1/subscriptions

Create with a catalog price (or items[]) and either a stored payment_method or a hosted payment link when omitted.

Request
POST /api/v1/subscriptions
Authorization: Bearer {rc_sk_...}

{
  "customer": "customer-uuid",
  "price": "price-uuid",
  "payment_method": "pm-uuid",
  "start_date": "2026-03-01"
}

API v1 — List / Get Subscription

GET /api/v1/subscriptions

GET /api/v1/subscriptions/{subscription}

Optional list filters: customer, status (active, paused, canceled, completed, pending_payment_method).

API v1 — Cancel Subscription

DELETE /api/v1/subscriptions/{subscription} — cancel immediately (optional reason).

POST /api/v1/subscriptions/{subscription} — schedule or undo cancel at period end:

Request
{
  "cancel_at_period_end": true,
  "cancellation_details": { "comment": "Customer request" },
  "proration_behavior": "none"
}

API v1 — Pause Preview

POST /api/v1/subscriptions/{subscription}/pause/preview

Returns whether unused prepaid time remains and which pause choices are required before calling pause.

Request Body

FieldTypeRequiredDescription
modestringYesimmediate or period_end
Request
{
  "mode": "immediate"
}
Response — 200 OK (excerpt)
{
  "object": "subscription_pause_preview",
  "pause_mode": "immediate",
  "requires_unused_time_choice": true,
  "remaining_paid_days": 14,
  "unused_prepaid_amount": 11.30,
  "suggested_refund_amount": 11.30
}

API v1 — Pause Subscription

POST /api/v1/subscriptions/{subscription}/pause

mode defaults to immediate for backward compatibility. When prepaid time remains on an immediate pause, unused_time_behavior is required (it is not silently defaulted).

Request Body

FieldTypeRequiredDescription
modestringNoimmediate (default) or period_end
reasonstringNoOptional pause reason
unused_time_behaviorstringIf prepaid remainspreserve_time or external_refund (immediate only)
refund_confirmedbooleanIf external_refundMust be true
refund_referencestringIf external_refundNon-empty refund reference
refund_notestringNoOptional note
⚠️

For period_end, do not send unused_time_behavior or refund fields. Service continues until the period ends; automatic future billing stops after that.

Immediate — preserve remaining time
{
  "mode": "immediate",
  "unused_time_behavior": "preserve_time",
  "reason": "Temporary pause"
}
Immediate — external refund
{
  "mode": "immediate",
  "unused_time_behavior": "external_refund",
  "refund_confirmed": true,
  "refund_reference": "refund_txn_123",
  "reason": "Customer refunded unused time"
}
Period end
{
  "mode": "period_end",
  "reason": "Pause after current period"
}

API v1 — Undo Period-End Pause

POST /api/v1/subscriptions/{subscription}/pause-at-period-end/undo

Cancels a scheduled pause-at-period-end before it takes effect. No request body required.

API v1 — Resume Preview

POST /api/v1/subscriptions/{subscription}/resume/preview

Preview how resume will behave, including charge due today and quote_hash when a restart-today charge applies.

Request Body

FieldTypeRequiredDescription
billing_cycle_anchorstringYesnow → restart today (from_today); unchanged → original schedule
Request
{
  "billing_cycle_anchor": "now"
}
Response — 200 OK (excerpt)
{
  "object": "subscription_resume_preview",
  "resume_path": "from_today",
  "charge_due_today": 11.30,
  "payment_required_to_reactivate": true,
  "quote_hash": "abc123...",
  "new_period_start": "2026-08-13",
  "new_period_end": "2026-08-27"
}

API v1 — Resume Subscription

POST /api/v1/subscriptions/{subscription}/resume

Request Body

FieldTypeRequiredDescription
billing_cycle_anchorstringYesnow or unchanged
proration_behaviorstringYesMust be none
quote_hashstringWhen preview returns onePass the hash from resume preview; required for restart-today charges
idempotency_keystringNoAlso accepted via Idempotency-Key header
Preserve / original schedule
{
  "billing_cycle_anchor": "unchanged",
  "proration_behavior": "none"
}
Restart today (with quote)
{
  "billing_cycle_anchor": "now",
  "proration_behavior": "none",
  "quote_hash": "abc123...",
  "idempotency_key": "resume-attempt-1"
}
🛑

A stale quote_hash returns a conflict error — request a new resume preview. If payment is required and fails, the subscription stays paused until a successful resume charge.

Frequency Options

Sequence ValueLabelInterval
1WeeklyEvery 1 week
2Bi-WeeklyEvery 2 weeks
3MonthlyEvery 1 month
4QuarterlyEvery 3 months
5Twice a YearEvery 6 months
6AnnualEvery 12 months

Scheduling & Payment Queue

RapidCents uses a two-stage billing engine to process recurring payments:

Stage 1 — Subscription Scheduler

The handle:subscriptions command runs daily and evaluates all active subscriptions. For each subscription:

  • Calculates the next billing date from last_transaction_date (or start_date) and sequence
  • If today is the billing date and no queue entry exists, creates a PaymentQueue record with status PENDING
  • Skips subscriptions that have reached their installment limit

Stage 2 — Payment Queue Processor

The app:handle-payment-queue command processes all pending queue items:

  • Charges the stored card via Visa, Mastercard, Discover, or Amex
  • On success: marks the queue item as FULFILLED, increments number_of_paid_bills, sends success notification
  • On decline: increments the retry counter, updates last_failed_attempt_at, sends decline notification

Retry Logic

When a payment is declined, the system automatically retries based on a configurable strategy.

AttemptDelayDescription
1st attemptDay 0Initial charge on the billing date
2nd attempt+3 daysFirst retry after 3 days
3rd attempt+3 daysSecond retry after 6 days total
4th attempt+3 daysFinal retry after 9 days total

The retry strategy is encoded as "0 3 3 3" where each number represents the number of days before the next attempt. Both merchant and customer receive email notifications for each failed attempt.

Card on File

Recurring payments use securely stored card tokens. The card can be stored in two ways:

  • Merchant-entered card: The card is tokenized at subscription creation time and linked to the subscription.
  • Customer-entered card: The customer enters card details via the hosted page, which are tokenized and stored.
  • Existing payment method: Pass a stored payment_method id on create.

All subsequent charges use the stored card token — the raw card data is never stored or re-transmitted.

All Endpoints

Public API v1 (Secret Key)

MethodEndpointDescription
POST/api/v1/subscriptionsCreate subscription
GET/api/v1/subscriptionsList subscriptions
GET/api/v1/subscriptions/{id}Get subscription
POST/api/v1/subscriptions/{id}Cancel at period end / undo
DELETE/api/v1/subscriptions/{id}Cancel immediately
POST/api/v1/subscriptions/{id}/pause/previewPause preview
POST/api/v1/subscriptions/{id}/pausePause (immediate or period end)
POST/api/v1/subscriptions/{id}/pause-at-period-end/undoUndo scheduled pause
POST/api/v1/subscriptions/{id}/resume/previewResume preview
POST/api/v1/subscriptions/{id}/resumeResume subscription
GET/api/v1/subscriptions/{id}/paymentsList subscription payments

Notifications

EventRecipientSubjectContent
Subscription created (customer card)CustomerYour Recurring payment is ReadyAmount, frequency, “Pay Now” link
Subscription created (merchant card)CustomerYour Upcoming Recurring Payment & InvoiceAmount, frequency, PDF invoice attachment
Payment successCustomerYour Subscription Payment Was SuccessfulAmount, card, installment info, transaction date
Payment successMerchantSubscription Payment Received from {customer}Amount, card, installment info, transaction date
Payment declinedCustomerYour Subscription Payment Was DeclinedDecline reason, attempt count
Payment declinedMerchantSubscription Payment Failed for {customer}Decline reason, attempt count

Error Handling

HTTP CodeScenarioDescription
422Validation errorMissing or invalid required fields (e.g. unused_time_behavior, quote_hash)
409ConflictLifecycle conflict or stale resume quote_hash
402Payment required / declinedResume charge failed; subscription stays paused
404Not foundSubscription does not exist
403Contract unsignedLinked contract must be signed before activation
500Server errorUnexpected processing error