UUpward API
v1
POST/api/public/v1/billing/subscription
Requires x-auth-token

Set a plan

Record the plan someone bought in your own payment system.

Before you call

  • Sign Up / Payment Link must be turned on in White Label settings.
  • Identify the member with x-auth-user (their pagename or your own external_id) alongside your API key.
  • Call List plans to see which plan_id values your company offers.
  • The company owner's plan can't be changed via the API — use the company billing settings.
  • If two active members share an external_id the call returns 409; use the pagename instead.

Reporting payments (paid through, renewals and failed payments)

  • paid_through: the date your payment covers. It becomes the end of the paid period, and Upward never extends it on its own: the member returns to the Free plan when it passes, unless you report the next payment first. Without it, Upward works out one period from the start and assumes the plan renews until you report a cancellation.
  • A renewal: call again with the same plan_id, the same billing_mode and the new paid_through. The dates are updated in place and the member's plan history is not reset (renewed: true in the response).
  • A failed payment: call with the current plan_id and billing_mode and payment_status: "failed". The plan shows as past due and keeps working until the paid-through date, then the member returns to Free. A later call with paid_through clears the failure.
  • paid_through must be a future date and cannot be combined with at_period_end or a failed payment (400).

Safe retries

  • Send an Idempotency-Key header (1–255 characters, new for every new change, e.g. a UUID). Repeating the same request with the same key returns the first result with the header Idempotent-Replayed: true and changes nothing.
  • The same key with a different request returns 422. While the first request is still running a repeat returns 409; retry shortly. Results are kept for 48 hours. A failure on our side (5xx) or a rate limit (429) is not stored, so you can retry with the same key.

When the new plan starts

  • Nothing sent: starts immediately.
  • effective_from: starts at that date/time.
  • effective_from can't be later than the current subscription's end time — anything later is set to that end time.
  • at_period_end: true: starts when the current paid period ends; the response has scheduled: true and startedAt is the switch date. With no active paid period it applies immediately.
  • at_period_end defaults to false.
  • Send effective_from or at_period_end: true, never both (400).

Body fields

plan_idstringrequired

Use a plan_id from List plans, e.g. starter.

billing_modemonthly | annualoptional

Defaults to monthly.

effective_fromstring (ISO 8601)optional

Date/time the new plan starts. Optional — defaults to now. Cannot be later than the current subscription's end time; a later value is set to that end time. Do not combine with at_period_end: true.

at_period_endbooleanoptional

Default: false (switch now or at effective_from). true = switch when the current paid period ends; the current plan runs until then. Do not combine with effective_from.

referencestringoptional

Your own order or invoice number, stored on the history record.

paid_throughstring (ISO 8601)optional

The date your payment covers (end of the paid period). Must be in the future. Sent again with the same plan it records a renewal in place. Upward does not extend it: the member returns to Free when it passes unless you report the next payment first. Do not combine with at_period_end or a failed payment.

payment_statuspaid | failedoptional

Defaults to paid. failed reports a failed payment on the current plan (same plan_id and billing_mode): it is shown as past due and keeps access until the paid-through date, then the member returns to Free.

Responses

200OKThe plan is active and the change is recorded.
{
  "success": true,
  "pagename": "johndoe",
  "planKey": "starter",
  "planName": "Starter",
  "startedAt": "2026-01-05T10:00:00.000Z",
  "renewsAt": "2026-02-05T10:00:00.000Z",
  "scheduled": false
}

With paid_through (a renewal of the same plan adds "renewed": true):
{
  "success": true,
  "pagename": "johndoe",
  "planKey": "starter",
  "planName": "Starter",
  "startedAt": "2026-01-05T10:00:00.000Z",
  "renewsAt": "2026-03-05T00:00:00.000Z",
  "scheduled": false,
  "paidThrough": "2026-03-05T00:00:00.000Z",
  "paymentStatus": "ok",
  "renewed": true
}
400Conflicting fieldsBoth effective_from and at_period_end were sent.
{ "success": false, "error": "Send either effective_from or at_period_end, not both." }
409External billing offThe company has not enabled its own payment links.
{ "success": false, "error": "Turn on Sign Up / Payment Link in White Label settings before using the billing API." }
422Idempotency-Key reusedThe key was already used with a different request.
{ "success": false, "error": "This Idempotency-Key was already used with a different request. Use a new key for a new request." }
409Company ownerThe company owner's plan cannot be changed via the API.
{ "success": false, "error": "The company owner's plan cannot be changed via the API; manage it from the company billing settings." }
POST/api/public/v1/billing/subscription

Use a plan_id from List plans, e.g. starter.

Defaults to monthly.

Date/time the new plan starts. Optional — defaults to now. Cannot be later than the current subscription's end time; a later value is set to that end time. Do not combine with at_period_end: true.

Default: false (switch now or at effective_from). true = switch when the current paid period ends; the current plan runs until then. Do not combine with effective_from.

Your own order or invoice number, stored on the history record.

The date your payment covers (end of the paid period). Must be in the future. Sent again with the same plan it records a renewal in place. Upward does not extend it: the member returns to Free when it passes unless you report the next payment first. Do not combine with at_period_end or a failed payment.

Defaults to paid. failed reports a failed payment on the current plan (same plan_id and billing_mode): it is shown as past due and keeps access until the paid-through date, then the member returns to Free.

ResponseNot sent yet