Skip to main content
POST
/
v2
/
payment-links
/
{id}
/
cancel

Cancel Payment Link

Cancel an active payment link to prevent it from being used. Only links with active status can be cancelled.

Authentication

Authorization: Bearer glm_test_YOUR_API_KEY

Path Parameters

ParameterTypeRequiredDescription
idstringYesPayment link ID (e.g., plink_abc123xyz)

Request Body

{
  "reason": "Customer requested cancellation"
}
ParameterTypeRequiredDescription
reasonstringNoReason for cancellation (max 500 chars)

Request

POST /v2/payment-links/{id}/cancel

Response

{
  "id": "plink_abc123xyz",
  "url": "https://checkout.withgale.com/pay/plink_abc123xyz",
  "amount_cents": 4995,
  "currency": "USD",
  "description": "Premium Blood Pressure Monitor",
  "payment_type": "one_time",
  "status": "cancelled",
  "customer": {
    "email": "customer@example.com",
    "first_name": "Jane",
    "last_name": "Doe"
  },
  "success_url": "https://yoursite.com/success",
  "cancel_url": "https://yoursite.com/cancel",
  "metadata": {
    "order_id": "ORD-12345"
  },
  "expires_at": "2025-11-02T14:30:00Z",
  "created_at": "2025-10-18T14:30:00Z",
  "cancelled_at": "2025-10-18T16:00:00Z",
  "cancellation_reason": "Customer requested cancellation"
}

Response Fields

FieldTypeDescription
statusenumWill be cancelled
cancelled_attimestampWhen link was cancelled
cancellation_reasonstringReason provided for cancellation
All other fields match the standard payment link object.

Examples

Basic Cancellation

curl -X POST https://api.withgale.com/v2/payment-links/plink_abc123xyz/cancel \
  -H "Authorization: Bearer glm_test_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "Order cancelled by customer"
  }'

With JavaScript

const cancelPaymentLink = async (linkId, reason) => {
  const response = await fetch(
    `https://api.withgale.com/v2/payment-links/${linkId}/cancel`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.GALE_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ reason })
    }
  );

  if (!response.ok) {
    throw new Error('Failed to cancel payment link');
  }

  const link = await response.json();
  console.log(`Link cancelled at ${link.cancelled_at}`);
  return link;
};

// Usage
await cancelPaymentLink('plink_abc123xyz', 'Customer changed mind');

Without Reason

curl -X POST https://api.withgale.com/v2/payment-links/plink_abc123xyz/cancel \
  -H "Authorization: Bearer glm_test_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Use Cases

Customer Cancellation Request

// Allow customer to cancel pending payment
app.post('/orders/:linkId/cancel', async (req, res) => {
  try {
    // Verify customer owns this link
    const link = await getPaymentLink(req.params.linkId);

    if (link.customer.email !== req.user.email) {
      return res.status(403).json({ error: 'Unauthorized' });
    }

    if (link.status !== 'active') {
      return res.status(400).json({
        error: `Cannot cancel ${link.status} payment link`
      });
    }

    // Cancel the link
    await cancelPaymentLink(req.params.linkId, 'Customer requested cancellation');

    res.json({ message: 'Payment link cancelled' });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});
// Cancel old active links that are about to expire
const cleanupOldLinks = async () => {
  const links = await listPaymentLinks({
    status: 'active',
    created_before: new Date(Date.now() - 14 * 24 * 60 * 60 * 1000) // 14 days ago
  });

  for (const link of links.data) {
    await cancelPaymentLink(link.id, 'Automatic cleanup of old link');
  }

  console.log(`Cancelled ${links.data.length} old links`);
};

Inventory Management

// Cancel payment link when product goes out of stock
const handleOutOfStock = async (productId) => {
  // Find all active payment links for this product
  const links = await listPaymentLinks({ status: 'active' });

  const productLinks = links.data.filter(link =>
    link.products.some(p => p.merchant_product_id === productId)
  );

  // Cancel each link
  for (const link of productLinks) {
    await cancelPaymentLink(link.id, 'Product out of stock');

    // Notify customer
    await sendEmail({
      to: link.customer.email,
      subject: 'Payment Link Cancelled',
      body: `Your payment link has been cancelled because the product is out of stock.`
    });
  }
};
// Cancel previous link when creating a new one for same order
const createReplacementLink = async (oldLinkId, newAmount) => {
  // Cancel old link
  await cancelPaymentLink(oldLinkId, 'Replaced with updated link');

  // Create new link
  const newLink = await createPaymentLink({
    amount_cents: newAmount,
    // ... other params
  });

  return newLink;
};

Webhooks

Cancelling a payment link triggers the following webhook event:
{
  "type": "payment_link.cancelled",
  "data": {
    "id": "plink_abc123xyz",
    "status": "cancelled",
    "cancelled_at": "2025-10-18T16:00:00Z",
    "cancellation_reason": "Customer requested cancellation"
  }
}
See Webhooks Reference for details.

Errors

Status CodeError CodeDescription
400invalid_stateLink is not in cancellable state
401unauthorizedInvalid or missing API key
404not_foundPayment link not found
429rate_limit_exceededToo many requests
{
  "error": {
    "code": "invalid_state",
    "message": "Cannot cancel payment link with status 'paid'",
    "param": "status"
  }
}
{
  "error": {
    "code": "invalid_state",
    "message": "Cannot cancel payment link with status 'expired'",
    "param": "status"
  }
}
Current StatusCan Cancel?Result
activeYesStatus changes to cancelled
paidNoError: Use refund endpoint instead
expiredNoError: Already expired
cancelledNoError: Already cancelled

Best Practices

Check Status First

Verify link is active before attempting to cancel

Provide Reasons

Always include a cancellation reason for audit trail

Notify Customers

Send email notification when cancelling customer’s link

Handle Webhooks

Listen for payment_link.cancelled events

Important Notes

Cannot cancel paid links. If a payment link has already been paid, you must create a Refund instead of cancelling the link.
Cancelled links cannot be reactivated. Once cancelled, a payment link cannot be used again. Create a new link if needed.