Recurring PaymentsSubscriptionsInitialize Subscription

Initialize a Subscription

Initialize a subscription after you have created the customer and plan. The customer and plan must belong to your account and to the environment selected by your secret key.

Endpoint

POST https://backendapi.sayswitchgroup.com/api/v1/subscription

Idempotency Key

Every initialization request requires an Idempotency-Key header:

Idempotency-Key: email-subscription-test-001

Generate a unique value for the subscription operation and keep it when retrying the same request. Repeating a successful request with the same key returns the existing subscription instead of creating a duplicate.

An idempotency key can be up to 100 characters and is scoped to your account and test or live environment. Use a new key when you intend to create a different subscription.

Request

curl --request POST \
  --url https://backendapi.sayswitchgroup.com/api/v1/subscription \
  --header "Authorization: Bearer YOUR_SECRET_KEY" \
  --header "Idempotency-Key: email-subscription-test-001" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --data '{
    "customer": "CUS_customer_code",
    "plan_code": "PLN_plan_code",
    "start_date": "2026-10-01T00:00:00+01:00",
    "return_url": "https://merchant.example/subscription/complete",
    "metadata": {
      "merchant_order_id": "EMAIL-ORDER-001"
    }
  }'

Request Fields

FieldTypeRequiredDescription
customerstringYesAn existing customer code or customer email belonging to your account.
plan_codestringYesAn active plan code belonging to your account.
start_dateISO 8601 date-timeNoWhen billing should begin. If omitted, billing begins immediately after authorization.
return_urlURLNoYour page to which the customer can be redirected after hosted authorization.
metadataobjectNoYour own structured data, returned in subscription responses and lifecycle webhooks.

Start Date

Use an ISO 8601 date and time with a timezone. For example, midnight in Lagos on October 1, 2026 is:

2026-10-01T00:00:00+01:00

If start_date is omitted, the first charge is due immediately after successful card authorization. A future date delays the first charge until that date.

Return URL

return_url belongs to your website, not SaySwitch. When applicable, the hosted authorization flow redirects the customer back with subscription_code and status query parameters:

https://merchant.example/subscription/complete?subscription_code=SUB_example&status=active

Live subscriptions require an HTTPS return URL. Test subscriptions accept HTTP or HTTPS. A browser redirect is not final proof of activation or payment; retrieve the subscription and process webhooks before granting access or fulfilling an order.

Response

A newly initialized subscription returns HTTP 201.

{
  "status": true,
  "message": "Subscription initialized. Customer authorization is required.",
  "data": {
    "id": 84,
    "business_id": 169,
    "customer_id": 4512,
    "recurring_plan_id": 42,
    "recurring_payment_method_id": null,
    "subscription_code": "SUB_7q2m9x4k1v8b5n3p6r0s",
    "return_url": "https://merchant.example/subscription/complete",
    "status": "pending_authorization",
    "start_at": "2026-09-30T23:00:00.000000Z",
    "next_charge_at": null,
    "authorization_expires_at": "2026-09-26T10:30:00.000000Z",
    "authorization_started_at": null,
    "cancelled_at": null,
    "successful_charge_count": 0,
    "metadata": {
      "merchant_order_id": "EMAIL-ORDER-001"
    },
    "domain": "test",
    "created_at": "2026-09-25T10:30:00.000000Z",
    "updated_at": "2026-09-25T10:30:00.000000Z",
    "plan": {
      "plan_code": "PLN_plan_code",
      "name": "Email Monthly",
      "amount": "15000.00",
      "currency": "NGN",
      "interval": "monthly",
      "invoice_limit": 12,
      "status": "active"
    },
    "customer": {
      "id": 4512,
      "email": "customer@example.com",
      "first_name": "Ada",
      "last_name": "Okafor",
      "customer_code": "CUS_customer_code",
      "domain": "test"
    },
    "payment_method": null,
    "authorization_url": "https://checkout.sayswitchgroup.com/pay/subscription/SUBINIT_example"
  }
}

The related plan and customer objects may contain additional non-sensitive account fields. Redirect the customer to the exact URL in data.authorization_url; do not build this URL yourself.

Replaying the same initialized operation with the same idempotency key returns HTTP 200 with:

{
  "status": true,
  "message": "Existing subscription returned for this idempotency key.",
  "data": {}
}

The replayed data object contains the same subscription structure. If the subscription is still pending_authorization, authorization_url is returned; for other statuses it is null.

Common Errors

Missing Idempotency Key

HTTP 422:

{
  "status": false,
  "message": "Invalid subscription details.",
  "errors": {
    "idempotency_key": [
      "The idempotency key field is required."
    ]
  }
}

Customer Not Found

HTTP 422:

{
  "message": "The selected customer does not exist.",
  "errors": {
    "customer": [
      "The selected customer does not exist."
    ]
  }
}

Plan Not Found or Inactive

HTTP 422:

{
  "message": "The selected plan is invalid or inactive.",
  "errors": {
    "plan_code": [
      "The selected plan is invalid or inactive."
    ]
  }
}

These responses are also used when the customer or plan belongs to a different merchant or test/live environment.

Invalid Start Date

HTTP 422:

{
  "status": false,
  "message": "Invalid subscription details.",
  "errors": {
    "start_date": [
      "The start date is not a valid date."
    ]
  }
}

Invalid Return URL

HTTP 422 in live mode when the URL is not HTTPS:

{
  "status": false,
  "message": "Invalid subscription details.",
  "errors": {
    "return_url": [
      "The return URL must use HTTPS for live subscriptions."
    ]
  }
}

Validation errors return HTTP 422, missing resources return HTTP 404, and missing or invalid secret-key authentication returns HTTP 401.