List Payment Links

The List Payment Links endpoint retrieves all payment links associated with your application. It supports pagination and automatically filters results to show only payment links that belong to your authenticated application, ensuring data isolation and security.

Endpoint DetailsCopied!

  • Method: GET

  • URL: /api/v0/payment-links

  • Content-Type: application/json

  • Authentication: Required (x-client-key and x-client-secret)

  • Scope Required: payment_link:list

Request HeadersCopied!

Header

Type

Required

Description

x-client-key

string

Yes

Your API client key

x-client-secret

string

Yes

Your API client secret

Query ParametersCopied!

Parameter

Type

Required

Default

Description

skip

integer

No

0

Number of records to skip (must be non-negative)

take

integer

No

10

Number of records to return (must be positive, max recommended: 100)

Pagination Guidelines

  • skip: Use for offset-based pagination (0, 10, 20, etc.)

  • take: Controls page size, keep reasonable (≤100) for performance

  • Maximum take: While not enforced, values >100 may impact performance

ResponseCopied!

Success Response (200 OK)

Returns an array of payment link objects:

[
  {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "title": "Premium Subscription",
    "description": "Monthly access to all premium features",
    "url": "premium-subscription",
    "linkType": "PRODUCT",
    "status": "ACTIVE",
    "amount": 29.99,
    "currency": "usdc",
    "recurringType": "MONTHLY",
    "walletId": "wallet_550e8400-e29b-41d4-a716-446655440000",
    "appId": "app_550e8400-e29b-41d4-a716-446655440000",
    "allowQuantityAdjustment": true,
    "collectTax": false,
    "collectAddress": true,
    "requirePhoneNumber": true,
    "limitPayments": false,
    "maxPayments": null,
    "allowBusinessTaxId": false,
    "allowMobilePayment": true,
    "confirmationPage": "SHOW",
    "createInvoicePdf": true,
    "allowCryptoPayment": true,
    "isForAllProduct": false,
    "customFields": null,
    "expiration_date": null,
    "customerId": null,
    "taxId": null,
    "createdAt": "2024-01-15T10:30:00Z",
    "coverImage": "https://example.com/images/premium.jpg",
    "paymentForId": null
  },
  {
    "id": "550e8400-e29b-41d4-a716-446655440001",
    "title": "Startup Bundle",
    "description": "Everything you need to get started",
    "url": "startup-bundle",
    "linkType": "PRODUCT",
    "status": "ACTIVE",
    "amount": null,
    "currency": "usdc",
    "recurringType": "ONE_TIME",
    "walletId": "wallet_550e8400-e29b-41d4-a716-446655440000",
    "appId": "app_550e8400-e29b-41d4-a716-446655440000",
    "allowQuantityAdjustment": true,
    "collectTax": true,
    "collectAddress": true,
    "requirePhoneNumber": false,
    "limitPayments": true,
    "maxPayments": 100,
    "allowBusinessTaxId": true,
    "allowMobilePayment": false,
    "confirmationPage": "SHOW",
    "createInvoicePdf": false,
    "allowCryptoPayment": true,
    "isForAllProduct": false,
    "customFields": {
      "company_size": "required",
      "use_case": "optional"
    },
    "expiration_date": "2024-12-31T23:59:59Z",
    "customerId": null,
    "taxId": "tax_550e8400-e29b-41d4-a716-446655440000",
    "createdAt": "2024-01-10T08:15:00Z",
    "coverImage": null,
    "paymentForId": "bundle_2024"
  }
]

Empty Response

When no payment links exist:

[]

Error Responses

400 Bad Request - Invalid Pagination
{
  "statusCode": 400,
  "message": "Skip parameter must be a non-negative number",
  "error": "Bad Request"
}
{
  "statusCode": 400,
  "message": "Take parameter must be a positive number",
  "error": "Bad Request"
}
401 Unauthorized - Invalid Authentication
{
  "statusCode": 401,
  "message": "Application not authenticated",
  "error": "Unauthorized"
}
403 Forbidden - Insufficient Scope
{
  "statusCode": 403,
  "message": "Insufficient scope for this operation",
  "error": "Forbidden"
}
429 Too Many Requests
{
  "statusCode": 429,
  "message": "Rate limit exceeded",
  "error": "Too Many Requests"
}

Example RequestsCopied!

Basic List Request

curl -X GET "https://api.devdraft.com/api/v0/payment-links" \
  -H "x-client-key: your_client_key" \
  -H "x-client-secret: your_client_secret"

Paginated Request

curl -X GET "https://api.devdraft.com/api/v0/payment-links?skip=20&take=10" \
  -H "x-client-key: your_client_key" \
  -H "x-client-secret: your_client_secret"

First Page (Initial Load)

curl -X GET "https://api.devdraft.com/api/v0/payment-links?skip=0&take=25" \
  -H "x-client-key: your_client_key" \
  -H "x-client-secret: your_client_secret"

Response FieldsCopied!

Core Information

Field

Type

Description

id

string

Unique payment link identifier

title

string

Display title of the payment link

description

string

Detailed description (nullable)

url

string

URL slug for the payment link

createdAt

datetime

Creation timestamp

Payment Configuration

Field

Type

Description

linkType

enum

Type of payment link (PRODUCT, DONATION, SUBSCRIPTION, etc.)

status

enum

Current status (ACTIVE, DEACTIVATED)

amount

number

Fixed amount (nullable for product bundles)

currency

enum

Payment currency (usdc, eurc)

recurringType

enum

Payment frequency

expiration_date

datetime

Link expiration (nullable)

Associated Resources

Field

Type

Description

walletId

string

Associated wallet identifier

appId

string

Your application identifier

customerId

string

Specific customer ID (nullable)

taxId

string

Tax configuration ID (nullable)

paymentForId

string

External reference ID (nullable)

Customer Experience Settings

Field

Type

Description

allowQuantityAdjustment

boolean

Can customers adjust quantities

collectTax

boolean

Tax collection enabled

collectAddress

boolean

Address collection required

requirePhoneNumber

boolean

Phone number required

allowBusinessTaxId

boolean

Business tax ID collection

allowMobilePayment

boolean

Mobile payment options enabled

allowCryptoPayment

boolean

Cryptocurrency payments enabled

Advanced Settings

Field

Type

Description

limitPayments

boolean

Payment count limited

maxPayments

number

Maximum payment count (nullable)

isForAllProduct

boolean

Includes all products from catalog

confirmationPage

enum

Post-payment page behavior

createInvoicePdf

boolean

Generate PDF receipts

customFields

object

Custom form fields (nullable)

coverImage

string

Cover image URL (nullable)

Integration PatternsCopied!

Basic Pagination Implementation

async function getAllPaymentLinks() {
  const allLinks = [];
  let skip = 0;
  const take = 25;
  let hasMore = true;

  while (hasMore) {
    const response = await fetch(
      `/api/v0/payment-links?skip=${skip}&take=${take}`,
      {
        headers: {
          'x-client-key': 'your_client_key',
          'x-client-secret': 'your_client_secret'
        }
      }
    );

    const links = await response.json();
    allLinks.push(...links);
    
    // If we got fewer results than requested, we've reached the end
    hasMore = links.length === take;
    skip += take;
  }

  return allLinks;
}

React Component Example

import { useState, useEffect } from 'react';

function PaymentLinksList() {
  const [links, setLinks] = useState([]);
  const [loading, setLoading] = useState(true);
  const [page, setPage] = useState(0);
  const pageSize = 10;

  useEffect(() => {
    fetchPaymentLinks();
  }, [page]);

  const fetchPaymentLinks = async () => {
    setLoading(true);
    try {
      const response = await fetch(
        `/api/v0/payment-links?skip=${page * pageSize}&take=${pageSize}`,
        {
          headers: {
            'x-client-key': process.env.REACT_APP_CLIENT_KEY,
            'x-client-secret': process.env.REACT_APP_CLIENT_SECRET
          }
        }
      );
      
      const data = await response.json();
      setLinks(data);
    } catch (error) {
      console.error('Failed to fetch payment links:', error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      {loading ? (
        <div>Loading...</div>
      ) : (
        <div>
          {links.map(link => (
            <div key={link.id} className="payment-link-card">
              <h3>{link.title}</h3>
              <p>{link.description}</p>
              <span className={`status ${link.status.toLowerCase()}`}>
                {link.status}
              </span>
              <div className="link-type">{link.linkType}</div>
              {link.amount && (
                <div className="amount">
                  {link.amount} {link.currency.toUpperCase()}
                </div>
              )}
            </div>
          ))}
          
          <div className="pagination">
            <button 
              onClick={() => setPage(p => Math.max(0, p - 1))}
              disabled={page === 0}
            >
              Previous
            </button>
            <span>Page {page + 1}</span>
            <button 
              onClick={() => setPage(p => p + 1)}
              disabled={links.length < pageSize}
            >
              Next
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

Filtering and Search

// Client-side filtering example
function filterPaymentLinks(links, filters) {
  return links.filter(link => {
    // Filter by status
    if (filters.status && link.status !== filters.status) {
      return false;
    }
    
    // Filter by type
    if (filters.linkType && link.linkType !== filters.linkType) {
      return false;
    }
    
    // Search by title
    if (filters.search && 
        !link.title.toLowerCase().includes(filters.search.toLowerCase())) {
      return false;
    }
    
    // Filter by currency
    if (filters.currency && link.currency !== filters.currency) {
      return false;
    }
    
    return true;
  });
}

// Usage
const filteredLinks = filterPaymentLinks(allLinks, {
  status: 'ACTIVE',
  linkType: 'PRODUCT',
  search: 'subscription',
  currency: 'usdc'
});

Performance ConsiderationsCopied!

Optimal Page Sizes

Use Case

Recommended take

Reasoning

Dashboard lists

10-25

Quick loading, good UX

Data tables

25-50

Balance between performance and content

Bulk operations

50-100

Efficient for processing

Mobile apps

10-20

Limited screen space

Caching Strategy

class PaymentLinksCache {
  constructor() {
    this.cache = new Map();
    this.cacheTimeout = 5 * 60 * 1000; // 5 minutes
  }
  
  getCacheKey(skip, take) {
    return `links_${skip}_${take}`;
  }
  
  async getPaymentLinks(skip = 0, take = 10) {
    const cacheKey = this.getCacheKey(skip, take);
    const cached = this.cache.get(cacheKey);
    
    if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
      return cached.data;
    }
    
    const response = await fetch(
      `/api/v0/payment-links?skip=${skip}&take=${take}`,
      {
        headers: {
          'x-client-key': 'your_client_key',
          'x-client-secret': 'your_client_secret'
        }
      }
    );
    
    const data = await response.json();
    
    this.cache.set(cacheKey, {
      data,
      timestamp: Date.now()
    });
    
    return data;
  }
  
  invalidateCache() {
    this.cache.clear();
  }
}

Error Handling Best PracticesCopied!

async function fetchPaymentLinksWithRetry(skip = 0, take = 10, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch(
        `/api/v0/payment-links?skip=${skip}&take=${take}`,
        {
          headers: {
            'x-client-key': 'your_client_key',
            'x-client-secret': 'your_client_secret'
          }
        }
      );
      
      if (response.status === 429) {
        // Rate limited - wait and retry
        const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }
      
      return await response.json();
      
    } catch (error) {
      if (attempt === maxRetries) {
        throw new Error(`Failed to fetch payment links after ${maxRetries} attempts: ${error.message}`);
      }
      
      // Wait before retry
      await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
    }
  }
}

Rate LimitingCopied!

  • Limit: 100 requests per minute per API key

  • Headers: Monitor X-RateLimit-* headers

  • Best Practice: Implement caching and pagination to minimize requests

Security FeaturesCopied!

  • App Isolation: Results automatically filtered by your application

  • No Cross-App Data: Cannot access other applications' payment links

  • Authentication Required: All requests must include valid API credentials

  • Scope Validation: Requires payment_link:list scope

Common Use CasesCopied!

Dashboard Integration

Display all payment links in your admin dashboard with pagination and search functionality.

Analytics & Reporting

Fetch all payment links to generate performance reports and usage analytics.

Bulk Operations

Retrieve payment links for bulk status updates, expiration management, or cleanup operations.

Mobile Applications

Implement efficient pagination for mobile apps with limited screen space.

Third-party Integrations

Sync payment link data with external CRM, accounting, or marketing systems.