API Reference
The Good Plan API allows you to programmatically create payment plans from your invoicing system, CRM, or other business tools.
Authentication
All API requests require authentication using an API key passed as a Bearer token.
Getting Your API Key
- Log into Good Plan
- Navigate to Dashboard → Webhooks
- Click "Generate API Key"
- Copy the key immediately (it's only shown once)
Your API key will look like: gp_a1b2c3d4e5f6... (64 characters)
Using Your API Key
Include the API key in the Authorization header:
Authorization: Bearer gp_your_api_key_here
Endpoints
Create Plan
Endpoint: POST /api/v1/plans
Creates a new payment plan with customizable options. Customer receives an email with a portal link to choose their preferred payment schedule.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
customer.name |
string | ✅ | Customer's full name |
customer.email |
string | ✅ | Customer's email address |
customer.phone |
string | ❌ | Customer's phone number |
invoice.amount |
number | ✅ | Amount owed (minimum 1.00) |
invoice.invoice_number |
string | ❌ | Your invoice/reference number |
invoice.description |
string | ❌ | Description of what's owed |
options.intervals |
array | ❌ | Payment frequencies: weekly, biweekly, monthly |
options.durations |
array | ❌ | Number of payments: 2-96 |
send_introduction_email |
boolean | ❌ | Send intro email to customer (default: true) |
About send_introduction_email:
- When
true(default), the customer receives an email with their portal link - When
false, no email is sent - use this if you want to send the portal link yourself - The
customer_portal_urlis always returned in the response regardless of this setting
Minimal Request Example
{
"customer": {
"name": "John Doe",
"email": "john@example.com"
},
"invoice": {
"amount": 1200.00
}
}
Full Request Example
{
"customer": {
"name": "John Doe",
"email": "john@example.com",
"phone": "+1-555-123-4567"
},
"invoice": {
"amount": 1200.00,
"invoice_number": "INV-2024-001",
"description": "Past due consulting services"
},
"options": {
"intervals": ["weekly", "monthly"],
"durations": [3, 6, 12]
},
"send_introduction_email": true
}
Success Response (201 Created)
{
"plan_id": 42,
"customer_portal_url": "https://goodplan.com/plan/abc123...",
"plan_options": [
{
"id": 101,
"installments": 3,
"interval": "monthly",
"payment_amount": "400.00",
"first_payment_date": "2025-12-04",
"final_payment_date": "2026-02-04"
}
],
"customer": {
"id": 567,
"name": "John Doe",
"email": "john@example.com"
},
"invoice": {
"id": 890,
"amount": "1200.00",
"invoice_number": "INV-2024-001"
}
}
Error Responses
401 Unauthorized - Missing or invalid API key:
{
"error": "API key required",
"message": "Please provide an API key in the Authorization header"
}
422 Validation Error - Invalid request data:
{
"error": "Validation failed",
"errors": {
"customer.email": ["The customer.email must be a valid email address."],
"invoice.amount": ["The invoice.amount must be at least 1."]
}
}
403 Forbidden - Plan limit reached:
{
"error": "Plan limit reached",
"message": "Your subscription plan limit has been reached."
}
Health Check
Endpoint: GET /api/health
Verify API availability (no authentication required).
Response (200 OK):
{
"status": "ok",
"version": "1.0.0",
"timestamp": "2025-11-27T01:15:30Z"
}
Code Examples
cURL
curl -X POST https://goodplan.com/api/v1/plans \
-H "Authorization: Bearer gp_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"customer": {
"name": "John Doe",
"email": "john@example.com"
},
"invoice": {
"amount": 1200.00,
"invoice_number": "INV-2024-001"
}
}'
PHP
$apiKey = 'gp_your_api_key_here';
$data = [
'customer' => [
'name' => 'John Doe',
'email' => 'john@example.com',
],
'invoice' => [
'amount' => 1200.00,
'invoice_number' => 'INV-2024-001',
],
];
$ch = curl_init('https://goodplan.com/api/v1/plans');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode($data),
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 201) {
$result = json_decode($response, true);
echo "Portal URL: " . $result['customer_portal_url'];
}
Python
import requests
api_key = 'gp_your_api_key_here'
url = 'https://goodplan.com/api/v1/plans'
headers = {
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
}
data = {
'customer': {
'name': 'John Doe',
'email': 'john@example.com'
},
'invoice': {
'amount': 1200.00,
'invoice_number': 'INV-2024-001'
}
}
response = requests.post(url, headers=headers, json=data)
if response.status_code == 201:
result = response.json()
print(f"Portal URL: {result['customer_portal_url']}")
Integration Patterns
Automatic Plan Creation
Create plans automatically when invoices become overdue:
- Check for overdue invoices in your system
- For each overdue invoice, call the API to create a plan
- Send the
customer_portal_urlto your customer - Customer selects their preferred payment schedule
- Receive webhooks when payments are made
Zapier Integration
- In Zapier, choose "Webhooks by Zapier" as the action
- Select "POST"
- URL:
https://yourdomain.com/api/v1/plans - Add headers:
Authorization:Bearer {{your_api_key}}Content-Type:application/json
- Map your trigger fields to the request body
Rate Limiting
- Default: 60 requests per minute per API key
- Burst: 10 requests per second
Rate limit info is included in response headers:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
Best Practices
- Store API keys securely - Use environment variables, never commit to version control
- Handle errors gracefully - Check status codes and error messages
- Use timeouts - Set reasonable timeouts (30 seconds recommended)
- Validate data - Validate customer email and invoice amount before sending
- Test in development - Use the health check endpoint to verify connectivity
Next Steps
- Set up Webhooks to receive updates
- Configure Zapier integration
- Build custom integration in your invoicing system