Skip to main content

Create Subscription

Create a pending subscription and redirect the user to the payment page. This endpoint integrates with Dodo Payments to handle the payment process.

πŸ“ Endpoint

POST /subscriptions

πŸ“‹ Request Body

email
string
required
The email address for the subscription. This will be used to link the subscription to the user’s account.
product_id
string
required
The ID of the product/plan to subscribe to.

πŸ“€ Request Example

curl -X POST https://api.posthoot.com/subscriptions \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "product_id": "pro_monthly"
  }'
const response = await fetch('https://api.posthoot.com/subscriptions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    email: 'user@example.com',
    product_id: 'pro_monthly'
  })
});

const data = await response.json();
import requests

response = requests.post(
    'https://api.posthoot.com/subscriptions',
    json={
        'email': 'user@example.com',
        'product_id': 'pro_monthly'
    }
)

data = response.json()

πŸ“₯ Response

Success (200 OK)

{
  "payment_url": "https://pay.dodo.com/checkout/session_abc123",
  "subscription_id": "sub_456",
  "customer_id": "cus_789",
  "status": "pending",
  "expires_at": "2024-01-01T11:00:00Z"
}

Error Responses

Invalid Product (400 Bad Request)

{
  "error": "validation_error",
  "message": "Invalid product ID",
  "code": "INVALID_PRODUCT"
}

Invalid Email (400 Bad Request)

{
  "error": "validation_error",
  "message": "Invalid email format",
  "code": "INVALID_EMAIL"
}

Payment Error (500 Internal Server Error)

{
  "error": "payment_error",
  "message": "Failed to create payment session",
  "code": "PAYMENT_ERROR"
}

πŸ”„ Subscription Flow

1. Create Subscription

// Step 1: Create subscription
const subscriptionResponse = await fetch('/subscriptions', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    email: 'user@example.com',
    product_id: 'pro_monthly'
  })
});

const subscription = await subscriptionResponse.json();

2. Redirect to Payment

// Step 2: Redirect user to payment page
window.location.href = subscription.payment_url;

3. Handle Payment Completion

// Step 3: User completes payment and returns
// The subscription will be automatically activated via webhook
// Step 4: When user registers/logs in, subscription is linked
const loginResponse = await fetch('/auth/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    email: 'user@example.com',
    password: 'password123'
  })
});

const loginData = await loginResponse.json();
// Subscription is automatically linked to the user's team

πŸ’³ Available Products

Pro Monthly

{
  "id": "pro_monthly",
  "name": "Pro Plan",
  "price": 29.99,
  "interval": "monthly",
  "features": [
    "email_campaigns",
    "template_library",
    "basic_analytics",
    "contact_management"
  ]
}

Pro Yearly

{
  "id": "pro_yearly",
  "name": "Pro Plan (Yearly)",
  "price": 299.99,
  "interval": "yearly",
  "features": [
    "email_campaigns",
    "template_library",
    "basic_analytics",
    "contact_management"
  ],
  "discount": "17%"
}

Enterprise

{
  "id": "enterprise",
  "name": "Enterprise Plan",
  "price": 99.99,
  "interval": "monthly",
  "features": [
    "email_campaigns",
    "template_library",
    "advanced_analytics",
    "contact_management",
    "automation",
    "api_access",
    "priority_support"
  ]
}

πŸ” Security Features

Payment Security

  • PCI Compliance: All payments processed through Dodo Payments
  • Encryption: Payment data encrypted in transit and at rest
  • Tokenization: Sensitive data tokenized for security

Subscription Security

  • Email Verification: Subscription linked to verified email
  • Session Expiration: Payment sessions expire after 1 hour
  • Fraud Protection: Advanced fraud detection systems

πŸ“Š Subscription States

Pending

  • Payment session created
  • Waiting for user to complete payment
  • Expires after 1 hour

Active

  • Payment completed successfully
  • Features enabled for the team
  • Billing cycle started

Canceled

  • Subscription canceled by user
  • Features disabled at end of billing period
  • No further charges

Paused

  • Payment failed or paused
  • Features temporarily disabled
  • Retry payment to reactivate

πŸ› οΈ Implementation Examples

React Component

import React, { useState } from 'react';

function SubscriptionForm() {
  const [email, setEmail] = useState('');
  const [productId, setProductId] = useState('pro_monthly');
  const [loading, setLoading] = useState(false);

  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);

    try {
      const response = await fetch('/subscriptions', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, product_id: productId })
      });

      if (response.ok) {
        const data = await response.json();
        window.location.href = data.payment_url;
      } else {
        const error = await response.json();
        alert(error.message);
      }
    } catch (error) {
      alert('Failed to create subscription');
    } finally {
      setLoading(false);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Enter your email"
        required
      />
      
      <select value={productId} onChange={(e) => setProductId(e.target.value)}>
        <option value="pro_monthly">Pro Monthly ($29.99)</option>
        <option value="pro_yearly">Pro Yearly ($299.99)</option>
        <option value="enterprise">Enterprise ($99.99)</option>
      </select>
      
      <button type="submit" disabled={loading}>
        {loading ? 'Creating...' : 'Subscribe Now'}
      </button>
    </form>
  );
}

Node.js Server

const express = require('express');
const app = express();

app.post('/subscriptions', async (req, res) => {
  try {
    const { email, product_id } = req.body;
    
    // Validate input
    if (!email || !product_id) {
      return res.status(400).json({
        error: 'validation_error',
        message: 'Email and product_id are required'
      });
    }
    
    // Create subscription via Posthoot API
    const response = await fetch('https://api.posthoot.com/subscriptions', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, product_id })
    });
    
    const data = await response.json();
    
    if (response.ok) {
      res.json(data);
    } else {
      res.status(response.status).json(data);
    }
  } catch (error) {
    res.status(500).json({
      error: 'server_error',
      message: 'Failed to create subscription'
    });
  }
});

πŸ”„ Webhook Integration

Payment Success Webhook

app.post('/webhook/payment-success', (req, res) => {
  const { subscription_id, customer_id, status } = req.body;
  
  if (status === 'active') {
    // Update subscription status in your database
    updateSubscriptionStatus(subscription_id, 'active');
    
    // Send welcome email
    sendWelcomeEmail(customer_id);
  }
  
  res.status(200).send('OK');
});