Update Payment Link

The Update Payment Link endpoint allows you to modify existing payment links while preserving their core functionality. You can update most configuration settings, product associations, customer experience options, and payment parameters. The system includes comprehensive validation and audit trail logging to track all changes.

Endpoint DetailsCopied!

  • Method: PUT

  • URL: /api/v0/payment-links/{id}

  • Content-Type: application/json

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

  • Scope Required: payment_link:update

Request HeadersCopied!

Header

Type

Required

Description

x-client-key

string

Yes

Your API client key

x-client-secret

string

Yes

Your API client secret

Content-Type

string

Yes

Must be application/json

Path ParametersCopied!

Parameter

Type

Required

Description

id

string

Yes

The unique UUID of the payment link to update

Request BodyCopied!

All fields are optional - only include fields you want to update:

Core Fields

Field

Type

Description

Constraints

title

string

Display title

3-100 characters

description

string

Detailed description

Max 500 characters, supports markdown

coverImage

string

Cover image URL

Valid URL format

linkType

enum

Type of payment link

Cannot be changed after creation

amount

number

Fixed amount

Min 0.01, null for product bundles

Payment Configuration

Field

Type

Description

currency

enum

Payment currency (usdc, eurc)

recurringType

enum

Payment frequency

expiration_date

datetime

Payment link expiration

paymentForId

string

External reference ID

walletId

string

Specific wallet ID

Product Configuration

Field

Type

Description

paymentLinkProducts

array

Array of products with quantities

isForAllProduct

boolean

Include all products from catalog

allowQuantityAdjustment

boolean

Allow customers to adjust quantities

Customer Data Collection

Field

Type

Description

collectAddress

boolean

Require customer address

requirePhoneNumber

boolean

Require phone number

allowBusinessTaxId

boolean

Allow business tax ID input

customFields

object

Custom form fields

Tax Configuration

Field

Type

Description

collectTax

boolean

Apply tax calculation

taxId

string

Tax configuration ID

Payment Limits & Controls

Field

Type

Description

limitPayments

boolean

Limit total number of payments

maxPayments

number

Maximum payment count

customerId

string

Restrict to specific customer

Payment Methods & Experience

Field

Type

Description

allowMobilePayment

boolean

Enable mobile payments

allowCryptoPayment

boolean

Enable cryptocurrency payments

confirmationPage

enum

Post-payment page behavior

createInvoicePdf

boolean

Generate PDF receipts

Update RestrictionsCopied!

Protected Fields

The following fields cannot be updated after creation:

  • url - URL slug is permanent

  • appId - Application association is fixed

  • linkType - Payment link type cannot be changed

  • createdAt - Creation timestamp is immutable

Business Logic Constraints

  • Active Payment Links: Some changes may be restricted if the payment link has active transactions

  • Tax Requirements: If collectTax is true, taxId must be provided and valid

  • Payment Limits: If limitPayments is true, maxPayments must be specified and greater than 0

  • Product Validation: All products in paymentLinkProducts must exist and belong to your app

Example RequestsCopied!

Update Basic Information

curl -X PUT "https://api.devdraft.com/api/v0/payment-links/550e8400-e29b-41d4-a716-446655440000" \
  -H "x-client-key: your_client_key" \
  -H "x-client-secret: your_client_secret" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Premium Subscription - Updated",
    "description": "Monthly access to all premium features with enhanced support",
    "amount": 34.99
  }'

Update Product Bundle

curl -X PUT "https://api.devdraft.com/api/v0/payment-links/550e8400-e29b-41d4-a716-446655440001" \
  -H "x-client-key: your_client_key" \
  -H "x-client-secret: your_client_secret" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Startup Bundle - Special Offer",
    "paymentLinkProducts": [
      {
        "productId": "123e4567-e89b-12d3-a456-426614174003",
        "quantity": 2
      },
      {
        "productId": "123e4567-e89b-12d3-a456-426614174005",
        "quantity": 1
      }
    ],
    "limitPayments": true,
    "maxPayments": 50
  }'

Enable Tax Collection

curl -X PUT "https://api.devdraft.com/api/v0/payment-links/550e8400-e29b-41d4-a716-446655440000" \
  -H "x-client-key: your_client_key" \
  -H "x-client-secret: your_client_secret" \
  -H "Content-Type: application/json" \
  -d '{
    "collectTax": true,
    "taxId": "tax_550e8400-e29b-41d4-a716-446655440000",
    "collectAddress": true,
    "requirePhoneNumber": true
  }'

Update Expiration and Payment Methods

curl -X PUT "https://api.devdraft.com/api/v0/payment-links/550e8400-e29b-41d4-a716-446655440000" \
  -H "x-client-key: your_client_key" \
  -H "x-client-secret: your_client_secret" \
  -H "Content-Type: application/json" \
  -d '{
    "expiration_date": "2025-06-30T23:59:59Z",
    "allowMobilePayment": true,
    "allowCryptoPayment": false,
    "customFields": {
      "department": {
        "type": "select",
        "required": true,
        "options": ["Sales", "Marketing", "Engineering", "Support"]
      }
    }
  }'

ResponseCopied!

Success Response (200 OK)

Returns the updated payment link with all current values:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "title": "Premium Subscription - Updated",
  "description": "Monthly access to all premium features with enhanced support",
  "url": "premium-subscription",
  "linkType": "PRODUCT",
  "status": "ACTIVE",
  "amount": 34.99,
  "currency": "usdc",
  "recurringType": "MONTHLY",
  "walletId": "wallet_550e8400-e29b-41d4-a716-446655440000",
  "appId": "app_550e8400-e29b-41d4-a716-446655440000",
  "createdAt": "2024-01-15T10:30:00Z",
  "expiration_date": "2025-06-30T23:59:59Z",
  "coverImage": "https://example.com/images/premium-subscription.jpg",
  "paymentForId": "sub_premium_2024",
  "customerId": null,
  "taxId": "tax_550e8400-e29b-41d4-a716-446655440000",
  "allowQuantityAdjustment": true,
  "collectTax": true,
  "collectAddress": true,
  "requirePhoneNumber": true,
  "limitPayments": false,
  "maxPayments": null,
  "allowBusinessTaxId": false,
  "allowMobilePayment": true,
  "allowCryptoPayment": false,
  "confirmationPage": "SHOW",
  "createInvoicePdf": true,
  "isForAllProduct": false,
  "customFields": {
    "department": {
      "type": "select",
      "required": true,
      "options": ["Sales", "Marketing", "Engineering", "Support"]
    }
  }
}

Error Responses

404 Not Found - Payment Link Not Found
{
  "statusCode": 404,
  "message": "Payment link not found",
  "error": "Not Found"
}
401 Unauthorized - Access Denied
{
  "statusCode": 401,
  "message": "Access denied to this payment link",
  "error": "Unauthorized"
}
400 Bad Request - Validation Error
{
  "statusCode": 400,
  "message": [
    "Title must be at least 3 characters long",
    "Tax with ID 123e4567-e89b-12d3-a456-426655440000 not found"
  ],
  "error": "Bad Request"
}
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"
}

Integration PatternsCopied!

Partial Updates

async function updatePaymentLinkTitle(paymentLinkId, newTitle) {
  try {
    const response = await fetch(`/api/v0/payment-links/${paymentLinkId}`, {
      method: 'PUT',
      headers: {
        'x-client-key': 'your_client_key',
        'x-client-secret': 'your_client_secret',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        title: newTitle
      })
    });

    if (response.ok) {
      const updatedPaymentLink = await response.json();
      console.log('Payment link updated:', updatedPaymentLink);
      return updatedPaymentLink;
    } else {
      const error = await response.json();
      console.error('Update failed:', error);
      throw new Error(error.message);
    }
  } catch (error) {
    console.error('Request failed:', error);
    throw error;
  }
}

Bulk Configuration Update

async function updatePaymentLinkConfiguration(paymentLinkId, config) {
  const updateData = {
    collectTax: config.enableTax,
    collectAddress: config.requireAddress,
    requirePhoneNumber: config.requirePhone,
    allowMobilePayment: config.enableMobile,
    limitPayments: config.hasLimit,
    ...( config.hasLimit && { maxPayments: config.paymentLimit } ),
    ...( config.enableTax && { taxId: config.taxConfigId } )
  };

  try {
    const response = await fetch(`/api/v0/payment-links/${paymentLinkId}`, {
      method: 'PUT',
      headers: {
        'x-client-key': 'your_client_key',
        'x-client-secret': 'your_client_secret',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(updateData)
    });

    if (response.ok) {
      return await response.json();
    } else {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
  } catch (error) {
    console.error('Configuration update failed:', error);
    throw error;
  }
}

// Usage
await updatePaymentLinkConfiguration('payment-link-id', {
  enableTax: true,
  taxConfigId: 'tax-id',
  requireAddress: true,
  requirePhone: false,
  enableMobile: true,
  hasLimit: true,
  paymentLimit: 100
});

Product Bundle Management

class PaymentLinkProductManager {
  constructor(clientKey, clientSecret) {
    this.clientKey = clientKey;
    this.clientSecret = clientSecret;
  }

  async addProduct(paymentLinkId, productId, quantity = 1) {
    // First, get current products
    const paymentLink = await this.fetchPaymentLink(paymentLinkId);
    const currentProducts = paymentLink.paymentLinkProducts || [];
    
    // Add new product
    const updatedProducts = [
      ...currentProducts,
      { productId, quantity }
    ];

    return this.updateProducts(paymentLinkId, updatedProducts);
  }

  async removeProduct(paymentLinkId, productId) {
    const paymentLink = await this.fetchPaymentLink(paymentLinkId);
    const updatedProducts = paymentLink.paymentLinkProducts.filter(
      p => p.productId !== productId
    );

    return this.updateProducts(paymentLinkId, updatedProducts);
  }

  async updateProductQuantity(paymentLinkId, productId, newQuantity) {
    const paymentLink = await this.fetchPaymentLink(paymentLinkId);
    const updatedProducts = paymentLink.paymentLinkProducts.map(p =>
      p.productId === productId ? { ...p, quantity: newQuantity } : p
    );

    return this.updateProducts(paymentLinkId, updatedProducts);
  }

  async updateProducts(paymentLinkId, products) {
    const response = await fetch(`/api/v0/payment-links/${paymentLinkId}`, {
      method: 'PUT',
      headers: {
        'x-client-key': this.clientKey,
        'x-client-secret': this.clientSecret,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        paymentLinkProducts: products
      })
    });

    if (!response.ok) {
      throw new Error(`Failed to update products: ${response.statusText}`);
    }

    return response.json();
  }

  async fetchPaymentLink(paymentLinkId) {
    const response = await fetch(`/api/v0/payment-links/${paymentLinkId}`, {
      headers: {
        'x-client-key': this.clientKey,
        'x-client-secret': this.clientSecret
      }
    });

    if (!response.ok) {
      throw new Error(`Failed to fetch payment link: ${response.statusText}`);
    }

    return response.json();
  }
}

Validation Before Update

async function validateUpdateData(paymentLinkId, updateData) {
  const errors = [];

  // Title validation
  if (updateData.title !== undefined) {
    if (typeof updateData.title !== 'string' || updateData.title.length < 3) {
      errors.push('Title must be at least 3 characters long');
    }
    if (updateData.title.length > 100) {
      errors.push('Title cannot exceed 100 characters');
    }
  }

  // Amount validation
  if (updateData.amount !== undefined) {
    if (typeof updateData.amount !== 'number' || updateData.amount <= 0) {
      errors.push('Amount must be a positive number');
    }
    if (updateData.amount < 0.01) {
      errors.push('Amount must be at least 0.01');
    }
  }

  // Tax validation
  if (updateData.collectTax === true && !updateData.taxId) {
    errors.push('Tax ID is required when tax collection is enabled');
  }

  // Payment limits validation
  if (updateData.limitPayments === true) {
    if (!updateData.maxPayments || updateData.maxPayments < 1) {
      errors.push('Max payments must be specified and greater than 0 when limiting payments');
    }
  }

  // Product validation
  if (updateData.paymentLinkProducts) {
    if (!Array.isArray(updateData.paymentLinkProducts)) {
      errors.push('Payment link products must be an array');
    } else {
      updateData.paymentLinkProducts.forEach((product, index) => {
        if (!product.productId) {
          errors.push(`Product ${index + 1}: Product ID is required`);
        }
        if (product.quantity && (typeof product.quantity !== 'number' || product.quantity < 1)) {
          errors.push(`Product ${index + 1}: Quantity must be a positive number`);
        }
      });
    }
  }

  return errors;
}

// Usage
const updateData = {
  title: 'New Title',
  amount: 49.99,
  collectTax: true,
  taxId: 'tax-id'
};

const validationErrors = await validateUpdateData(paymentLinkId, updateData);
if (validationErrors.length > 0) {
  console.error('Validation failed:', validationErrors);
} else {
  const updatedPaymentLink = await updatePaymentLink(paymentLinkId, updateData);
}

Business LogicCopied!

Update Behavior

  • Partial Updates: Only fields included in the request body are updated

  • Null Values: Sending null for optional fields clears their values

  • Empty Strings: Converted to null for UUID fields (taxId, walletId, customerId)

  • Product Replacement: When updating paymentLinkProducts, the entire list is replaced

Audit Trail

All updates are logged with comprehensive audit trails including:

  • User Information: Who made the changes

  • Before/After Values: Complete change tracking

  • Significant Changes: Price changes >5%, status changes, etc.

  • Metadata: Request context and timestamps

Automatic Validation

The system automatically validates:

  • Tax Configuration: Ensures tax exists when collectTax is enabled

  • Product Existence: Verifies all products in bundles exist

  • Ownership: Confirms payment link belongs to your application

  • Business Rules: Enforces payment limit requirements

Rate LimitingCopied!

  • Limit: 100 requests per minute per API key

  • Headers: Monitor X-RateLimit-* headers

  • Best Practice: Batch multiple field updates into single requests

Security FeaturesCopied!

  • Ownership Validation: Can only update payment links belonging to your application

  • Field Protection: Prevents updates to immutable fields

  • Scope Enforcement: Requires payment_link:update scope

  • Input Sanitization: All input data is validated and sanitized

Common Use CasesCopied!

Price Updates

Modify pricing for subscription services or product offerings.

Configuration Changes

Update customer data collection requirements or payment method availability.

Product Bundle Management

Add, remove, or modify products in bundle payment links.

Seasonal Adjustments

Set expiration dates for promotional campaigns or limited-time offers.

Experience Optimization

Update descriptions, images, and custom fields based on customer feedback.