On this page
No Headings
Last updated: June 26, 2026
Integrate subscriptions to bill customers at regular intervals.
This client-side and server-side integration uses the following:
Use Postman to explore and test PayPal APIs. Learn more in our Postman guide
Pass the PayPal-Auth-Assertion header with the standard Content-Type, Authorization, and PayPal-Request-IDheaders. The value of the PayPal-Auth-Assertion header can be generated as follows:
// client-side JavaScript
function encodeObjectToBase64(object) {
const objectString = JSON.stringify(object);
return window.btoa(objectString);
}
const clientId = "CLIENT-ID";
const sellerPayerId = "SELLER-PAYER-ID"; // preferred
// const sellerEmail = "SELLER-ACCOUNT-EMAIL"; // use instead of payer-id if required
const header = {
alg: "none"
};
const encodedHeader = encodeObjectToBase64(header);
const payload = {
iss: clientId,
payer_id: sellerPayerId
// email: sellerEmail
};
const encodedPayload = encodeObjectToBase64(payload);
const jwt =`${encodedHeader}.${encodedPayload}.`; // json web token
console.log(`Paypal-Auth-Assertion=${jwt}`);Note: The token contains two period (.) characters, which are required according to the JSON web token structure.
clientID.sellerPayerId is the payer ID of the receiving seller's PayPal account. You can also use email instead of payer_idand supply the email address of the seller's PayPal account.Example functions to generate the PayPal-Auth-Assertion header in other programming environments:
// Node.js
function encodeObjectToBase64(object) {
const objectString = JSON.stringify(object);
return Buffer
.from(objectString)
.toString("base64");
}
const clientId = "CLIENT-ID";
const sellerPayerId = "SELLER-PAYER-ID"; // preferred
// const sellerEmail = "SELLER-ACCOUNT-EMAIL"; // use instead if payer-id unknown\n
const header = {
alg: "none"
};
const encodedHeader = encodeObjectToBase64(header);
const payload = {
iss: clientId,
payer_id: sellerPayerId
// email: sellerEmail
};
const encodedPayload = encodeObjectToBase64(payload);
const jwt = `${encodedHeader}.${encodedPayload}.`; // json web token
console.log(`Paypal-Auth-Assertion=${jwt}`);// Java
import org.apache.commons.codec.binary.Base64;
public class Base64Encode {
public static void main(String[] args) {
String clientId = "CLIENT-ID";
String sellerPayerId = "SELLER-PAYER-ID"; // preferred
// String sellerEmail = "SELLER-ACCOUNT-EMAIL"; // use instead if payer-id unknown\n
String header = "{\\"alg\\":\\"none\\"}";
String payload =
"{\\"iss\\":\\"" + clientId + "\\",\\"payer_id\\":\\"" + sellerPayerId + "\\"}";
// "{\"iss\":\"" + clientId + "\",\"email\":\"" + sellerEmail + "\"}";\n
byte[] encodedHeader = Base64.encodeBase64(header.getBytes());
byte[] encodedPayload = Base64.encodeBase64(payload.getBytes());\n
String jwt = new String(encodedHeader) +
"." +
new String(encodedPayload) +
"."; // json web token
System.out.println("Paypal-Auth-Assertion=" + jwt);
}
}For more information about request headers, see HTTP request headers.
To create a product for your subscription plan, copy and modify the following code:
API endpoint used: Create product
curl -v -X POST https://api-m.sandbox.paypal.com/v1/catalogs/products -H "Content-Type: application/json" -H "Authorization: Bearer ACCESS-TOKEN" -H "PayPal-Request-Id: REQUEST-ID" -H "PayPal-Auth-Assertion: AUTH-ASSERTION" -d '{
"name": "Video Streaming Service",
"description": "A video streaming service",
"type": "SERVICE",
"category": "SOFTWARE",
"image_url": "https://example.com/streaming.jpg",
"home_url": "https://example.com/home"
}'After you copy the code in the sample request, modify the following:
ACCESS-TOKEN to your access token.REQUEST-ID with a unique ID that you generate. This ID helps prevent duplicate requests if the API call is disrupted.AUTH-ASSERTION header to your JSON Web Token (JWT) assertion that identifies your seller. For more information on how to create a JWT, see PayPal-Auth-Assertion.name and description to represent your product.A successful request results in the following:
201 Created.id for the product. Use this ID to complete other actions through the REST API, such as creating a subscription plan.{
"id": "PROD-5FD60555F23244316",
"name": "Video Streaming Service",
"description": "A video streaming service",
"create_time": "2020-01-21T16:04:39Z",
"links": [
{
"href": "https://api-m.sandbox.paypal.com/v1/catalogs/products/PROD-5FD60555F23244316",
"rel": "self",
"method": "GET"
},
{
"href": "https://api-m.sandbox.paypal.com/v1/catalogs/products/PROD-5FD60555F23244316",
"rel": "edit",
"method": "PATCH"
}
]
}The following sample request is an example of a subscription plan. Modify the code to fit your subscription model.
Review the following topics to help understand how to modify the code for your use case:
This sample request creates a subscription plan that:
Important: Only one currency_code is allowed per subscription plan. Make a new subscription plan to offer a subscription in another currency.
API endpoint used: Create plan
curl -v -k -X POST https://api-m.sandbox.paypal.com/v1/billing/plans -H "Accept: application/json" -H "Authorization: Bearer <Access-Token>" -H "PayPal-Request-Id: 123e4567-e89b-12d3-a456-426655440020" -H "PayPal-Auth-Assertion: eyJhbGciOiJub25lIn0.eyJpc3MiOiJjbGllbnRfaWQiLCJlbWFpbCI6Im15LWVtYWlsQGV4YW1wbGUuY29tIn0." -d '{
"product_id": "PROD-5FD60555F23244316",
"name": "Basic Plan",
"description": "Basic plan",
"billing_cycles": [
{
"frequency": {
"interval_unit": "MONTH",
"interval_count": 1
},
"tenure_type": "TRIAL",
"sequence": 1,
"total_cycles": 1
},
{
"frequency": {
"interval_unit": "MONTH",
"interval_count": 1
},
"tenure_type": "REGULAR",
"sequence": 2,
"total_cycles": 12,
"pricing_scheme": {
"fixed_price": {
"value": "10",
"currency_code": "USD"
}
}
}
],
"payment_preferences": {
"auto_bill_outstanding": true,
"setup_fee": {
"value": "10",
"currency_code": "USD"
},
"setup_fee_failure_action": "CONTINUE",
"payment_failure_threshold": 3
},
"taxes": {
"percentage": "10",
"inclusive": false
}
}'After you copy the code in the sample request, modify the following:
ACCESS-TOKEN to your access token.REQUEST-ID with a unique ID that you generate. This ID helps prevent duplicate requests if the API call is disrupted.AUTH-ASSERTION header to your JSON Web Token (JWT) assertion that identifies your seller. For more information on how to create a JWT, see PayPal-Auth-Assertion.product_id parameter to the ID returned when you created the product.A successful request results in the following:
201 Created.id for the subscription plan. Use the subscription plan ID to complete other actions through the REST API, such as editing or deactivating the plan.On status.To see how the result of this API call looks in the seller's account, use your sandbox business account credentials to log in to https://www.sandbox.paypal.com/billing/plans. The subscription plan reflects the plan nu
{
"id": "P-17M15335A8501272JLXLLNKI",
"product_id": "PROD-5FD60555F23244316",
"name": "Basic Plan",
"status": "ACTIVE",
"description": "Basic plan",
"create_time": "2020-01-21T16:09:13Z",
"links": [
{
"href": "https://api-m.sandbox.paypal.com/v1/billing/plans/P-17M15335A8501272JLXLLNKI",
"rel": "self",
"method": "GET"
},
{
"href": "https://api-m.sandbox.paypal.com/v1/billing/plans/P-17M15335A8501272JLXLLNKI",
"rel": "edit",
"method": "PATCH"
},
{
"href": "https://api-m.sandbox.paypal.com/v1/billing/plans/P-17M15335A8501272JLXLLNKI/deactivate",
"rel": "self",
"method": "POST"
}
]
}Create a subscription for your plan.
API endpoint used: Create subscription
curl -v -X POST https://api-m.sandbox.paypal.com/v1/billing/subscriptions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <Access-Token>" \
-H "PayPal-Partner-Attribution-Id: Example_Marketplace" \
-H "PayPal-Request-Id: 123e4567-e89b-12d3-a456-426655440020" \
-H "PayPal-Auth-Assertion: eyJhbGciOiJub25lIn0.eyJpc3MiOiJjbGllbnRfaWQiLCJlbWFpbCI6Im15LWVtYWlsQGV4YW1wbGUuY29tIn0." \
-d '{
"plan_id": "P-17M15335A8501272JLXLLNKI",
"start_time": "2020-01-22T00:00:00Z",
"quantity": "20",
"shipping_amount": {
"currency_code": "USD",
"value": "10.00"
},
"subscriber": {
"name": {
"given_name": "John",
"surname": "Doe"
},
"email_address": "[email protected]",
"shipping_address": {
"name": {
"full_name": "John Doe"
},
"address": {
"address_line_1": "2211 N First Street",
"address_line_2": "Building 17",
"admin_area_2": "San Jose",
"admin_area_1": "CA",
"postal_code": "95131",
"country_code": "US"
}
}
},
"application_context": {
"brand_name": "example-retail",
"locale": "en-US",
"shipping_preference": "SET_PROVIDED_ADDRESS",
"user_action": "SUBSCRIBE_NOW",
"payment_method": {
"payer_selected": "PAYPAL",
"payee_preferred": "IMMEDIATE_PAYMENT_REQUIRED"
},
"return_url": "https://example.com/returnUrl",
"cancel_url": "https://example.com/cancelUrl"
}
}'After you copy the code in the sample request, modify the following:
Access-Token to your access token.PayPal-Partner-Attribution-Id to your BN code.PayPal-Request-Id with a unique ID you generate. This ID helps prevent creating duplicate products in the event that the API call is disrupted. See also: API Idempotency.PayPal-Auth-Assertion header with your own JSON Web Token (JWT) assertion that identifies your seller. For more information on how to create a JWT, see PayPal-Auth-Assertion.subscriber/shipping_address:
application_context/shipping_preference to NO_SHIPPING. This hides shipping information fields on the PayPal Review page.application_context/shipping preference blank or set it to GET_FROM_FILE.application_context/user_action field to automatically activate subscriptions. Set the field to SUBSCRIBE_NOW or send it empty. The default value is SUBSCRIBE_NOW. Otherwise, you need to make a POST v1/billing/subscriptions/{ID}/activate call to activate the subscription.{
"id": "I-BW452GLLEP1G",
"status": "APPROVAL_PENDING",
"status_update_time": "2018-12-10T21:20:49Z",
"plan_id": "P-17M15335A8501272JLXLLNKI",
"start_time": "2020-01-22T00:00:00Z",
"quantity": "20",
"shipping_amount": {
"currency_code": "USD",
"value": "10.00"
},
"subscriber": {
"name": {
"given_name": "John",
"surname": "Doe"
},
"email_address": "[email protected]",
"payer_id": "2J6QB8YJQSJRJ",
"shipping_address": {
"name": {
"full_name": "John Doe"
},
"address": {
"address_line_1": "2211 N First Street",
"address_line_2": "Building 17",
"admin_area_2": "San Jose",
"admin_area_1": "CA",
"postal_code": "95131",
"country_code": "US"
}
}
},
"create_time": "2018-12-10T21:20:49Z",
"links": [
{
"href": "https://www.paypal.com/webapps/billing/subscriptions?ba_token=BA-2M539689T3856352J",
"rel": "approve",
"method": "GET"
},
{
"href": "https://api-m.paypal.com/v1/billing/subscriptions/I-BW452GLLEP1G",
"rel": "edit",
"method": "PATCH"
},
{
"href": "https://api-m.paypal.com/v1/billing/subscriptions/I-BW452GLLEP1G",
"rel": "self",
"method": "GET"
}
]
}201 Created.id field of the API response.To start a subscription from your website, add the PayPal JavaScript SDK code and modify it. This code adds buttons to your website so your buyers can use PayPal or a debit or credit card.
<!DOCTYPE html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Ensures optimal rendering on mobile devices. -->
</head>
<body>
<script src="https://www.paypal.com/sdk/js?client-id=YOUR_CLIENT_ID&vault=true&intent=subscription">
</script> // Add your client_id
<div id="paypal-button-container"></div>
<script>
paypal.Buttons({
createSubscription: function(data, actions) {
return actions.subscription.create({
'plan_id': 'YOUR_PLAN_ID' // Creates the subscription
});
},
onApprove: function(data, actions) {
alert('You have successfully subscribed to ' + data.subscriptionID); // Optional message given to subscriber
}
}).render('#paypal-button-container'); // Renders the PayPal button
</script>
</body>
</html>YOUR_CLIENT_ID to your client ID.YOUR_PLAN_ID to the plan ID returned from the Create Plan API call.
Tip: To render more than one button on a single webpage, see Multiple subscribe buttons for your website.
Test a transaction to see the subscription created in the merchant account:
Select the PayPal button on the page.
Use the sandbox personal login information from the Developer Dashboard to log in and simulate the buyer making a purchase.
In the Checkout window, make a note of the purchase amount in the upper right corner. USD is the default currency. You can customize the JavaScript SDK by adding a different currency code
Availability: The JavaScript SDK onShippingChange, onShippingAddressChange, and onShippingOptionsChange functions are not compatible with Subscriptions.
Select the arrow next to the purchase amount to view the subscription details:
Select the test credit card as the payment method and select Continue.
Select Agree & Subscribe to agree to the terms of the subscription.
Use the Subscriptions API capabilities to customize your integrations.
You can run negative tests on your integration to manage the responses you give to your customers.
Go live