Scheduled Payments
Overview
ElasticPay supports two ways to schedule a payment for a future date:
- Payment Plans — the system automatically generates and processes recurring payment intents on your behalf (weekly, monthly, etc.)
- Direct PI scheduling — you create a single payment intent now and set
scheduled_payment_dateto a future date; the system processes it automatically on that date
This guide covers direct PI scheduling. For a full reference of payment plan lifecycle management, see Payment Plans API.
Requirements
- A saved payment method (
pm_xxx) must be attached to the intent at create time. Transient tokens (from the widget) are not accepted because the customer will not be present at processing time. - The date must be today or in the future, and at most 366 days from today.
Step 1 — Save a payment method
If you don’t have a saved pm_xxx yet, create a Setup Intent to collect and save the customer’s card:
curl -X POST https://api.elasticpay.co/api/v1/setup_intents \ -H "Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{"customer_id": "cus_0abc123def456"}'const res = await fetch("https://api.elasticpay.co/api/v1/setup_intents", { method: "POST", headers: { "Authorization": "Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "Content-Type": "application/json", }, body: JSON.stringify({ customer_id: "cus_0abc123def456" }),});const { client_secret } = await res.json();import requests
res = requests.post( "https://api.elasticpay.co/api/v1/setup_intents", headers={ "Authorization": "Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "Content-Type": "application/json", }, json={"customer_id": "cus_0abc123def456"},)client_secret = res.json()["client_secret"]<?phpuse GuzzleHttp\Client;
$client = new Client([ 'base_uri' => 'https://api.elasticpay.co', 'headers' => [ 'Authorization' => 'Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'Content-Type' => 'application/json', ],]);
$res = $client->post('/api/v1/setup_intents', [ 'json' => ['customer_id' => 'cus_0abc123def456'],]);$data = json_decode($res->getBody(), true);$client_secret = $data['client_secret'];require 'faraday'require 'json'
conn = Faraday.new('https://api.elasticpay.co') do |f| f.request :json f.response :jsonendconn.headers['Authorization'] = 'Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'conn.headers['Content-Type'] = 'application/json'
res = conn.post('/api/v1/setup_intents') do |req| req.body = { customer_id: 'cus_0abc123def456' }endclient_secret = res.body['client_secret']using System.Net.Http;using System.Net.Http.Headers;using System.Net.Http.Json;
using var http = new HttpClient { BaseAddress = new Uri("https://api.elasticpay.co") };http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
var res = await http.PostAsJsonAsync("/api/v1/setup_intents", new { customer_id = "cus_0abc123def456" });var si = await res.Content.ReadFromJsonAsync<JsonElement>();var clientSecret = si.GetProperty("client_secret").GetString();Pass the client_secret to the payment widget. After the customer completes the form, the saved method pm_xxx is available on the customer record.
Step 2 — Create a scheduled payment intent
curl -X POST https://api.elasticpay.co/api/v1/payment_intents \ -H "Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "amount": 9900, "currency": "AUD", "customer_id": "cus_0abc123def456", "payment_method": "pm_0xyz789abc123def456ghi012jkl", "scheduled_payment_date": "2026-07-01" }'const res = await fetch("https://api.elasticpay.co/api/v1/payment_intents", { method: "POST", headers: { "Authorization": "Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "Content-Type": "application/json", }, body: JSON.stringify({ amount: 9900, currency: "AUD", customer_id: "cus_0abc123def456", payment_method: "pm_0xyz789abc123def456ghi012jkl", scheduled_payment_date: "2026-07-01", }),});const pi = await res.json();import requests
res = requests.post( "https://api.elasticpay.co/api/v1/payment_intents", headers={ "Authorization": "Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "Content-Type": "application/json", }, json={ "amount": 9900, "currency": "AUD", "customer_id": "cus_0abc123def456", "payment_method": "pm_0xyz789abc123def456ghi012jkl", "scheduled_payment_date": "2026-07-01", },)pi = res.json()<?phpuse GuzzleHttp\Client;
$client = new Client([ 'base_uri' => 'https://api.elasticpay.co', 'headers' => [ 'Authorization' => 'Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'Content-Type' => 'application/json', ],]);
$res = $client->post('/api/v1/payment_intents', [ 'json' => [ 'amount' => 9900, 'currency' => 'AUD', 'customer_id' => 'cus_0abc123def456', 'payment_method' => 'pm_0xyz789abc123def456ghi012jkl', 'scheduled_payment_date' => '2026-07-01', ],]);$pi = json_decode($res->getBody(), true);require 'faraday'require 'json'
conn = Faraday.new('https://api.elasticpay.co') do |f| f.request :json f.response :jsonendconn.headers['Authorization'] = 'Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'conn.headers['Content-Type'] = 'application/json'
res = conn.post('/api/v1/payment_intents') do |req| req.body = { amount: 9900, currency: 'AUD', customer_id: 'cus_0abc123def456', payment_method: 'pm_0xyz789abc123def456ghi012jkl', scheduled_payment_date: '2026-07-01' }endpi = res.bodyusing System.Net.Http;using System.Net.Http.Headers;using System.Net.Http.Json;
using var http = new HttpClient { BaseAddress = new Uri("https://api.elasticpay.co") };http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
var res = await http.PostAsJsonAsync("/api/v1/payment_intents", new { amount = 9900, currency = "AUD", customer_id = "cus_0abc123def456", payment_method = "pm_0xyz789abc123def456ghi012jkl", scheduled_payment_date = "2026-07-01",});var pi = await res.Content.ReadFromJsonAsync<JsonElement>();The response will have status: "requires_confirmation" and scheduled_payment_date: "2026-07-01".
Step 3 — Confirm to lock in the schedule
curl -X POST https://api.elasticpay.co/api/v1/payment_intents/pi_0abc123/confirm \ -H "Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{}'const res = await fetch( "https://api.elasticpay.co/api/v1/payment_intents/pi_0abc123/confirm", { method: "POST", headers: { "Authorization": "Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "Content-Type": "application/json", }, body: JSON.stringify({}), });const result = await res.json();import requests
res = requests.post( "https://api.elasticpay.co/api/v1/payment_intents/pi_0abc123/confirm", headers={ "Authorization": "Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "Content-Type": "application/json", }, json={},)result = res.json()<?phpuse GuzzleHttp\Client;
$client = new Client([ 'base_uri' => 'https://api.elasticpay.co', 'headers' => [ 'Authorization' => 'Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'Content-Type' => 'application/json', ],]);
$res = $client->post('/api/v1/payment_intents/pi_0abc123/confirm', ['json' => []]);$result = json_decode($res->getBody(), true);require 'faraday'require 'json'
conn = Faraday.new('https://api.elasticpay.co') do |f| f.request :json f.response :jsonendconn.headers['Authorization'] = 'Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'conn.headers['Content-Type'] = 'application/json'
res = conn.post('/api/v1/payment_intents/pi_0abc123/confirm') do |req| req.body = {}endresult = res.bodyusing System.Net.Http;using System.Net.Http.Headers;using System.Net.Http.Json;
using var http = new HttpClient { BaseAddress = new Uri("https://api.elasticpay.co") };http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
var res = await http.PostAsJsonAsync( "/api/v1/payment_intents/pi_0abc123/confirm", new {});var result = await res.Content.ReadFromJsonAsync<JsonElement>();Because the date is in the future, the response status will be ready — the payment is queued for automatic processing on scheduled_payment_date. No further action is needed.
ready status
A payment intent in ready status has been confirmed and is waiting for its scheduled date.
| Status | Meaning |
|---|---|
ready | Confirmed, waiting for scheduled date |
processing | Submitted to PSP |
succeeded | Payment collected |
failed | Payment declined or errored |
Webhook events
Subscribe to these events to monitor scheduled payment activity:
| Event | When |
|---|---|
payment_intent.succeeded | Scheduled payment collected successfully |
payment_intent.failed | Payment declined or failed |
payment_intent.processing | Submitted to PSP, awaiting result |
See Webhooks for how to handle these events.