> ## Documentation Index
> Fetch the complete documentation index at: https://docs.paygentic.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Customer Lifecycle

> Complete guide to creating and managing customers through the API, including merchant-managed and full consumer accounts

The customer lifecycle in Paygentic involves creating customer relationships, establishing subscriptions, and tracking usage. This guide covers the programmatic approach to managing customers through the API.

## Overview

Paygentic supports two distinct types of consumer accounts, each with different capabilities and use cases:

<CardGroup cols={2}>
  <Card title="Managed Consumers" icon="building">
    Created via API by merchants for quick onboarding. Customers only access Paygentic in relation to this one merchant.
    These customer do not have Paygentic users and only use Paygentic through the merchant integration.
  </Card>

  <Card title="Full Consumer Accounts" icon="user">
    When the customer interacts with multiple merchants they can choose to keep everything in one account and sign in to Paygentic to manage their usage.
  </Card>
</CardGroup>

Normally customers will start off as a managed customer and only transition to a full account when needed.
The key point for merchants is that in managed accounts the account funds are exclusively available to that merchant.

## Creating customers via API

### Prerequisites

* Valid merchant API key
* Merchant account ID
* Customer email and preferable address

<Info>
  **API Reference**: See the [Create Customer API endpoint](/api-reference/customers/create-new-customer) for complete request and response schemas.
</Info>

<Tabs>
  <Tab title="New Managed Consumer">
    When creating a new customer with consumer data, you're establishing a managed consumer account:

    <CodeGroup>
      ```javascript JavaScript theme={null}
      const axios = require('axios');

      async function createManagedCustomer() {
        try {
          const response = await axios.post(
            'https://api.paygentic.io/v0/customers',
            {
              merchantId: 'org_YS8jkP59V71TdUvj',
              consumer: {
                name: 'John Doe',
                email: 'john.doe@example.com',
                phone: '+1-555-0100',
                address: {
                  line1: '123 Main St',
                  city: 'San Francisco',
                  state: 'CA',
                  country: 'US',
                  zipCode: '94105'
                }
              }
            },
            {
              headers: {
                'Authorization': 'Bearer sk_live_YOUR_API_KEY',
                'Content-Type': 'application/json'
              }
            }
          );

          console.log('Customer created:', response.data.customerId);
          return response.data.customerId;
        } catch (error) {
          console.error('Error creating customer:', error.response?.data);
          throw error;
        }
      }
      ```

      ```bash cURL theme={null}
      curl -X POST https://api.paygentic.io/v0/customers \
        -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "merchantId": "org_YS8jkP59V71TdUvj",
          "consumer": {
            "name": "John Doe",
            "email": "john.doe@example.com",
            "phone": "+1-555-0100",
            "address": {
              "line1": "123 Main St",
              "city": "San Francisco",
              "state": "CA",
              "country": "US",
              "zipCode": "94105"
            }
          }
        }'
      ```

      ```python Python theme={null}
      import requests

      def create_managed_customer():
          url = "https://api.paygentic.io/v0/customers"
          
          headers = {
              "Authorization": "Bearer sk_live_YOUR_API_KEY",
              "Content-Type": "application/json"
          }
          
          payload = {
              "merchantId": "org_YS8jkP59V71TdUvj",
              "consumer": {
                  "name": "John Doe",
                  "email": "john.doe@example.com",
                  "phone": "+1-555-0100",
                  "address": {
                      "line1": "123 Main St",
                      "city": "San Francisco",
                      "state": "CA",
                      "country": "US",
                      "zipCode": "94105"
                  }
              }
          }
          
          try:
              response = requests.post(url, json=payload, headers=headers)
              response.raise_for_status()
              
              customer_id = response.json()["customerId"]
              print(f"Customer created: {customer_id}")
              return customer_id
          except requests.exceptions.RequestException as e:
              print(f"Error creating customer: {e}")
              raise
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Existing Managed Consumer">
    If you've already created a managed consumer, you can reuse it:

    <CodeGroup>
      ```javascript JavaScript theme={null}
      async function createCustomerWithExistingConsumer() {
        try {
          const response = await axios.post(
            'https://api.paygentic.io/v0/customers',
            {
              merchantId: 'org_YS8jkP59V71TdUvj',
              consumerId: 'org_EXISTING_CONSUMER_ID' // Must be managed consumer
            },
            {
              headers: {
                'Authorization': 'Bearer sk_live_YOUR_API_KEY',
                'Content-Type': 'application/json'
              }
            }
          );

          return response.data.customerId;
        } catch (error) {
          // Error code 403: Attempting to use unauthorized consumerId
          if (error.response?.status === 403) {
            console.error('Cannot use this consumerId - not a managed consumer');
          }
          throw error;
        }
      }
      ```

      ```bash cURL theme={null}
      curl -X POST https://api.paygentic.io/v0/customers \
        -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "merchantId": "org_YS8jkP59V71TdUvj",
          "consumerId": "org_EXISTING_CONSUMER_ID"
        }'
      ```

      ```python Python theme={null}
      def create_customer_with_existing_consumer():
          url = "https://api.paygentic.io/v0/customers"
          
          headers = {
              "Authorization": "Bearer sk_live_YOUR_API_KEY",
              "Content-Type": "application/json"
          }
          
          payload = {
              "merchantId": "org_YS8jkP59V71TdUvj",
              "consumerId": "org_EXISTING_CONSUMER_ID"  # Must be managed consumer
          }
          
          try:
              response = requests.post(url, json=payload, headers=headers)
              response.raise_for_status()
              return response.json()["customerId"]
          except requests.exceptions.HTTPError as e:
              if e.response.status_code == 403:
                  print("Cannot use this consumerId - not a managed consumer")
              raise
      ```
    </CodeGroup>
  </Tab>
</Tabs>

<Note>
  **Idempotency**: If a customer already exists for the consumer-merchant pair, the API returns `200` with the existing customer ID instead of creating a duplicate.
</Note>

## Creating subscriptions

Once you have a customer, you can create subscriptions. The plan you choose determines which [fees](/platform/pricing/fees) and usage-based prices apply.

Prepaid fees need to be paid before the subscription starts. In the subscription response there will be a payment link where the customer can pay for this charge. The payment link is also present if the subscription has a "prefundAmount", this should be used to add funds for pay-as-you-go (instantly priced usage billable metrics).

<Info>
  **API Reference**: See the [Create Subscription API endpoint](/api-reference/subscription/create-new-subscription) for complete request and response schemas.
</Info>

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function createPostpaidSubscription(customerId, planId) {
    try {
      const response = await axios.post(
        'https://api.paygentic.io/v0/subscriptions',
        {
          customerId: customerId,
          planId: planId,
          name: 'Monthly API Service',
          startedAt: new Date().toISOString(),
          prefundAmount: "25"
        },
        {
          headers: {
            'Authorization': 'Bearer sk_live_YOUR_API_KEY',
            'Content-Type': 'application/json'
          }
        }
      );

      const subscription = response.data;
      console.log('Subscription created:', subscription.id);
      console.log('Payment link (if payment is required):', subscription.payment?.checkoutUrl);
      
      return subscription;
    } catch (error) {
      console.error('Error creating subscription:', error.response?.data);
      throw error;
    }
  }
  ```

  ```python Python theme={null}
  from datetime import datetime

  def create_postpaid_subscription(customer_id, plan_id):
      url = "https://api.paygentic.io/v0/subscriptions"
      
      headers = {
          "Authorization": "Bearer sk_live_YOUR_API_KEY",
          "Content-Type": "application/json"
      }
      
      payload = {
          "customerId": customer_id,
          "planId": plan_id,  # Must be a postpaid plan
          "name": "Monthly API Service",
          "startedAt": datetime.utcnow().isoformat() + "Z",
          "prefundAmount": "25"
      }
      
      try:
          response = requests.post(url, json=payload, headers=headers)
          response.raise_for_status()
          
          subscription = response.json()
          print(f"Subscription created: {subscription['id']}")
          print(f"Payment details (if payment is required): {subscription.get('payment')}")
          
          return subscription
      except requests.exceptions.RequestException as e:
          print(f"Error creating subscription: {e}")
          raise
  ```
</CodeGroup>

## Customer portal

The customer portal allows your customers to view their subscription details, usage metrics, invoices, and manage payment sources - all within a secure, hosted interface.

### Generating portal links

<Tabs>
  <Tab title="Managed Consumer Portal" icon="building">
    For managed consumers, generate portal links via the subscription endpoint:

    <Info>
      **API Reference**: See the [Generate Portal Link API endpoint](/api-reference/subscriptions/generate-customer-portal-access-link) for complete request and response schemas.
    </Info>

    <CodeGroup>
      ```javascript JavaScript theme={null}
      async function generatePortalLink(subscriptionId) {
        try {
          const response = await axios.post(
            `https://api.paygentic.io/v0/subscriptions/${subscriptionId}/portal`,
            {},
            {
              headers: {
                'Authorization': 'Bearer sk_live_YOUR_API_KEY',
                'Content-Type': 'application/json'
              }
            }
          );

          const portalUrl = response.data.url;
          console.log('Portal URL:', portalUrl);
          return portalUrl;
        } catch (error) {
          console.error('Error generating portal link:', error.response?.data);
          throw error;
        }
      }
      ```

      ```bash cURL theme={null}
      curl -X POST https://api.paygentic.io/v0/subscriptions/{subscriptionId}/portal \
        -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{}'
      ```

      ```python Python theme={null}
      import requests

      def generate_portal_link(subscription_id):
          url = f"https://api.paygentic.io/v0/subscriptions/{subscription_id}/portal"
          
          headers = {
              "Authorization": "Bearer sk_live_YOUR_API_KEY",
              "Content-Type": "application/json"
          }
          
          try:
              response = requests.post(url, json={}, headers=headers)
              response.raise_for_status()
              
              portal_url = response.json()["url"]
              print(f"Portal URL: {portal_url}")
              return portal_url
          except requests.exceptions.RequestException as e:
              print(f"Error generating portal link: {e}")
              raise
      ```
    </CodeGroup>

    This provides read-only access to subscription data, source management, and usage tracking.
  </Tab>

  <Tab title="Full Platform Access" icon="user">
    Full consumer accounts have complete platform access at `platform.paygentic.io` with:

    * Account management
    * Payment method management
    * Full subscription control
    * Usage history and invoices
  </Tab>
</Tabs>

### Embedding the portal

You can embed the customer portal directly into your application, providing a seamless experience without redirecting users away.

#### Setup requirements

Before embedding:

1. **Domain Whitelisting**: Contact [support@paygentic.io](mailto:support@paygentic.io) to whitelist your domain(s)
2. **CSP Configuration**: Add `platform.paygentic.io` to your CSP `frame-src` directive:

```text theme={null}
Content-Security-Policy: frame-src 'self' https://platform.paygentic.io;
```

#### Implementation

<CodeGroup>
  ```html HTML theme={null}
  <!DOCTYPE html>
  <html>
  <body>
    <iframe
      src="https://platform.paygentic.io/portal/access/PORTAL_ACCESS_TOKEN"
      width="100%"
      height="800px"
      sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
      style="border: none; border-radius: 8px;"
    ></iframe>
  </body>
  </html>
  ```

  ```jsx React theme={null}
  export function CustomerPortal({ portalUrl }) {
    return (
      <iframe
        src={portalUrl}
        width="100%"
        height="800px"
        sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
        style={{ border: 'none', borderRadius: '8px' }}
      />
    );
  }
  ```
</CodeGroup>

<Warning>
  **Important:**

  * Portal URLs are single-use - generate a fresh URL for each session or page reload
  * The `sandbox` attribute is required for security
</Warning>

<Accordion title="Understanding sandbox attributes">
  The `sandbox` attribute is required for security:

  * `allow-scripts` - Portal functionality
  * `allow-same-origin` - Communication with your site
  * `allow-forms` - Form submission
  * `allow-popups` - 3D Secure authentication
</Accordion>

<Info>
  **Payment Checkout**: To collect one-off payments or embed a payment form in your application, see [Payment Portal](/platform/payments/portal).
</Info>

## Next steps

* Collect one-off charges with the [Payments API](/platform/payments/overview)
* Learn about [Usage Events](/platform/billing/usage-events) for tracking consumption
* Explore [Pricing](/platform/pricing/overview) for billing configuration
* Understand [Accounts](/platform/payments/accounts) for payment management
