MyTPEMyTPE Pay
Pay Links

Create Pay Link

Create a new payment link with specific validity modes.

POST https://dev.mytpe.app/api/v2/mytpe-pay/links/store

Creates a new payment link for a Mytpe Pay instance.

Authentication: API Key (X-API-KEY + X-API-SECRET headers)

This API requires authentication via X-API-KEY and X-API-SECRET headers.

It is critical to understand the relationship between type and payment_mode.

  • Dynamic Links: You set a fixed price that the customer must pay. These support all 3 modes (reusable, one_shot, limited).
  • Static Links: The customer enters the amount (e.g., Donation). These are always Reusable. You cannot set them to one_shot or limited.

Validation Rule

If you send type: "static", the system will ignore any payment_mode or max_payments values you provide. These parameters are only respected when type is dynamic.

Validity & Modes (Dynamic Only)

For Dynamic links, you can define the lifecycle:

  • reusable (Default): The link never expires. Ideal for e-commerce products.
  • one_shot: The link accepts exactly 1 payment and auto-closes. Ideal for specific invoices.
  • limited: The link accepts a quota defined by max_payments. Ideal for event tickets.

Body Parameters

Prop

Type

Example Request

create-pay-link.js
const form = new FormData();
form.append('mytpe_pay_id', 'mytpe_pay_xyz789');
form.append('title', 'Limited Offer Product');
form.append('details', 'A special limited edition product.');
form.append('type', 'dynamic');
form.append('amount', '5000.00');
form.append('slug', 'limited-offer');
form.append('payment_mode', 'limited');
form.append('max_payments', '50');
form.append('metadata[internal_id]', 'PROD_123');
form.append('logo', document.querySelector('#logo').files[0]);

const response = await fetch('https://dev.mytpe.app/api/v2/mytpe-pay/links/store', {
  method: 'POST',
  headers: {
    'X-API-KEY': 'your_api_key',
    'X-API-SECRET': 'your_api_secret',
  },
  body: form,
});

const data = await response.json();
console.log(data);
create-pay-link.ts
interface PayLinkResponse {
  success: boolean;
  message: string;
  data: {
    id: string;
    title: string;
    slug: string;
    type: string;
    amount: string;
    status: string;
    payment_mode: string;
    max_payments: number | null;
    payment_link: string;
    payment_status: string;
  };
}

const form = new FormData();
form.append('mytpe_pay_id', 'mytpe_pay_xyz789');
form.append('title', 'Limited Offer Product');
form.append('details', 'A special limited edition product.');
form.append('type', 'dynamic');
form.append('amount', '5000.00');
form.append('slug', 'limited-offer');
form.append('payment_mode', 'limited');
form.append('max_payments', '50');
form.append('metadata[internal_id]', 'PROD_123');

const fileInput = document.querySelector<HTMLInputElement>('#logo');
if (fileInput?.files?.[0]) {
  form.append('logo', fileInput.files[0]);
}

const response = await fetch('https://dev.mytpe.app/api/v2/mytpe-pay/links/store', {
  method: 'POST',
  headers: {
    'X-API-KEY': 'your_api_key',
    'X-API-SECRET': 'your_api_secret',
  },
  body: form,
});

const data: PayLinkResponse = await response.json();
console.log(data);
create_pay_link.py
import requests

url = 'https://dev.mytpe.app/api/v2/mytpe-pay/links/store'

headers = {
    'X-API-KEY': 'your_api_key',
    'X-API-SECRET': 'your_api_secret',
}

payload = {
    'mytpe_pay_id': 'mytpe_pay_xyz789',
    'title': 'Limited Offer Product',
    'details': 'A special limited edition product.',
    'type': 'dynamic',
    'amount': '5000.00',
    'slug': 'limited-offer',
    'payment_mode': 'limited',
    'max_payments': '50',
    'metadata[internal_id]': 'PROD_123',
}

files = {
    'logo': ('logo.png', open('/path/to/logo.png', 'rb'), 'image/png'),
}

response = requests.post(url, headers=headers, data=payload, files=files)

data = response.json()
print(data)
CreatePayLinkController.php
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
    'X-API-KEY' => 'your_api_key',
    'X-API-SECRET' => 'your_api_secret',
])
->attach('logo', file_get_contents('/path/to/logo.png'), 'logo.png')
->post('https://dev.mytpe.app/api/v2/mytpe-pay/links/store', [
    'mytpe_pay_id' => 'mytpe_pay_xyz789',
    'title' => 'Limited Offer Product',
    'details' => 'A special limited edition product.',
    'type' => 'dynamic',
    'amount' => '5000.00',
    'slug' => 'limited-offer',
    'payment_mode' => 'limited',
    'max_payments' => 50,
    'metadata' => ['internal_id' => 'PROD_123'],
]);

$data = $response->json();
create-pay-link.php
<?php

$curl = curl_init();

$boundary = uniqid();
$delimiter = '-------------' . $boundary;

$postData = '';
$fields = [
    'mytpe_pay_id' => 'mytpe_pay_xyz789',
    'title' => 'Limited Offer Product',
    'details' => 'A special limited edition product.',
    'type' => 'dynamic',
    'amount' => '5000.00',
    'slug' => 'limited-offer',
    'payment_mode' => 'limited',
    'max_payments' => '50',
    'metadata[internal_id]' => 'PROD_123',
];

foreach ($fields as $name => $value) {
    $postData .= "--{$delimiter}\r\n";
    $postData .= "Content-Disposition: form-data; name=\"{$name}\"\r\n\r\n";
    $postData .= "{$value}\r\n";
}

$postData .= "--{$delimiter}\r\n";
$postData .= "Content-Disposition: form-data; name=\"logo\"; filename=\"logo.png\"\r\n";
$postData .= "Content-Type: image/png\r\n\r\n";
$postData .= file_get_contents('/path/to/logo.png') . "\r\n";
$postData .= "--{$delimiter}--\r\n";

curl_setopt_array($curl, [
    CURLOPT_URL => 'https://dev.mytpe.app/api/v2/mytpe-pay/links/store',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_POSTFIELDS => $postData,
    CURLOPT_HTTPHEADER => [
        'X-API-KEY: your_api_key',
        'X-API-SECRET: your_api_secret',
        'Content-Type: multipart/form-data; boundary=' . $delimiter,
    ],
]);

$response = curl_exec($curl);
curl_close($curl);

echo $response;
create-pay-link.cjs
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');

const form = new FormData();
form.append('mytpe_pay_id', 'mytpe_pay_xyz789');
form.append('title', 'Limited Offer Product');
form.append('details', 'A special limited edition product.');
form.append('type', 'dynamic');
form.append('amount', '5000.00');
form.append('slug', 'limited-offer');
form.append('payment_mode', 'limited');
form.append('max_payments', '50');
form.append('metadata[internal_id]', 'PROD_123');
form.append('logo', fs.createReadStream('/path/to/logo.png'));

axios.post('https://dev.mytpe.app/api/v2/mytpe-pay/links/store', form, {
  headers: {
    ...form.getHeaders(),
    'X-API-KEY': 'your_api_key',
    'X-API-SECRET': 'your_api_secret',
  },
})
.then(response => console.log(response.data))
.catch(error => console.error(error.response?.data));

Example Success Response (201 Created)

201 Created

{
    "success": true,
    "message": "Pay link created successfully",
    "data": {
        "mytpe_pay_id": "instance-001",
        "title": "Demo Product",
        "details": "A detailed description of the demo product.",
        "slug": "demo-product",
        "type": "dynamic",
        "amount": "2500.00",
        "status": "active",
        "logo": null,
        "payment_mode": "reusable",
        "max_payments": null,
        "successful_payments_count": 0,
        "metadata": {
            "internal_reference": "REF-123"
        },
        "id": "link-demo-123",
        "updated_at": "2026-01-17T10:00:00.000Z",
        "created_at": "2026-01-17T10:00:00.000Z",
        "payment_link": "https://frontdev.mytpe.app/pay/demo-product",
        "payment_status": "active",
        "remaining_payments": null
    }
}

Understanding payment_status

Newly created links will have one of these payment_status values:

  • active: Reusable or limited links ready to accept payments
  • awaiting_payment: One-shot links waiting for their first payment

The payment_status field automatically updates based on payment activity and helps you track the payment state of each link.

Error Responses

422 Validation Error

{
    "message": "The given data was invalid.",
    "errors": {
        "slug": ["The slug has already been taken."],
        "amount": ["The amount field is required when type is dynamic."]
    }
}

On this page