# PayLinks API Documentation — Full Content > This file contains the complete documentation for PayLinks (https://paylinks.ro), an agentic payments infrastructure platform built in Romania. > Base URL: https://api.paylinks.ro/api/v1 > Currencies: RON, EUR, GBP | Auth: JWT via magic link, Google OAuth, or API key exchange > AI integrations: MCP Server (61 public tools), WebMCP (36 tools), ChatGPT Actions --- # Getting Started Get up and running with the PayLinks API in under 5 minutes. ## 1. Get an API Token ### Option A: Magic Link (email) ```bash # Request a magic link curl -X POST https://api.paylinks.ro/api/v1/auth/request \ -H "Content-Type: application/json" \ -d '{"email": "you@example.com"}' ``` Check your inbox and click the link. The verification endpoint returns a JWT: ```json { "token": "eyJhbGciOiJIUzI1NiIs...", "needsOnboarding": false } ``` ### Option B: Google OAuth Visit `https://api.paylinks.ro/api/v1/auth/google/start` in your browser. After consent, you'll receive a JWT token. ### Option C: Programmatic signup (no web login) ```bash curl -X POST https://api.paylinks.ro/api/v1/auth/api/signup \ -H "Content-Type: application/json" \ -d '{"email":"dev@example.com","name":"Dev","keyName":"Primary integration"}' ``` This returns account details, first API key, and a short-lived JWT. ## 2. Create a Payment Link ```bash curl -X POST https://api.paylinks.ro/api/v1/paylinks \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -H "X-Currency: RON" \ -d '{ "name": "Premium Course", "slug": "premium-course", "priceType": "FIXED", "serviceType": "DIGITAL_PRODUCT", "paymentType": "ONE_TIME", "amount": 49.99, "collectEmail": true }' ``` Response: ```json { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "name": "Premium Course", "slug": "premium-course", "priceType": "FIXED", "paymentType": "ONE_TIME", "serviceType": "DIGITAL_PRODUCT", "amount": 49.99, "currency": "RON", "active": true, "createdAt": "2026-02-17T10:00:00.000Z" } ``` > **Note:** PayLink management endpoints expose amounts in major units (e.g. `49.99`). ## 3. Share the Link Your payment link is live at: ``` https://paylinks.ro/p/premium-course ``` Share it with your customers. They can pay with card via Stripe. ## 4. View in Dashboard Open [paylinks.ro/dashboard](https://paylinks.ro/dashboard) to see your transactions, analytics, and customer data. ## Next Steps - [Authentication](/authentication) — Magic link, Google OAuth, JWT details - [API Reference](/api-reference/overview) — Full endpoint documentation - [WebMCP Integration](/integrations/webmcp) — Use PayLinks with browser AI agents - [MCP Server](/integrations/mcp-server) — Connect PayLinks to Claude Desktop --- # Authentication PayLinks uses JWT bearer tokens for API authentication. Tokens are obtained via magic link (email), Google OAuth, or API key exchange. ## API Key Flow (Recommended for Integrations) ### 0. (Optional) Programmatic signup in one call ```bash curl -X POST https://api.paylinks.ro/api/v1/auth/api/signup \ -H "Content-Type: application/json" \ -d '{"email":"dev@example.com","name":"Dev","keyName":"Primary integration"}' ``` This returns account details, first API key, and a short-lived JWT. ### 1. Create an API key in Dashboard Go to **Dashboard -> Profile -> API Access** and create a key. You receive: - `keyId` (starts with `plk_`) - `keySecret` (starts with `pls_`, shown once) ### 2. Exchange API key for JWT ```bash curl -X POST https://api.paylinks.ro/api/v1/auth/api/token \ -H "Content-Type: application/json" \ -d '{"keyId":"plk_xxx","keySecret":"pls_xxx"}' ``` ```json { "token": "eyJhbGciOiJIUzI1NiIs...", "tokenType": "Bearer", "expiresInSeconds": 3600, "user": { "id": "user_123", "email": "you@example.com", "role": "USER" } } ``` ### 3. Use JWT as Bearer token ```bash curl https://api.paylinks.ro/api/v1/paylinks \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \ -H "X-Currency: RON" ``` ## Magic Link Flow ### 1. Request a Magic Link ```bash curl -X POST https://api.paylinks.ro/api/v1/auth/request \ -H "Content-Type: application/json" \ -d '{"email": "you@example.com"}' ``` ```json {"ok": true} ``` ### 2. User Clicks Email Link The email contains a link pointing to the verify endpoint with the `token` query parameter. ### 3. Verify and Get JWT ```bash curl "https://api.paylinks.ro/api/v1/auth/verify?token=MAGIC_TOKEN" ``` ```json { "token": "eyJhbGciOiJIUzI1NiIs...", "needsOnboarding": false } ``` ## Google OAuth Flow ### Standard OAuth 1. Redirect the user to: `GET /auth/google/start?redirectTo=https://yourapp.com/callback` 2. User completes Google consent 3. Google redirects to `/auth/google/callback` with `code` and `state` 4. The API returns a JWT token ### Google One Tap ```bash curl -X POST https://api.paylinks.ro/api/v1/auth/google/onetap \ -H "Content-Type: application/json" \ -d '{"credential": "GOOGLE_ONE_TAP_JWT"}' ``` ## Using the Token Include the JWT in the `Authorization` header for all protected endpoints: ```bash curl https://api.paylinks.ro/api/v1/me \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." ``` ## X-Currency Header Set the currency context with the `X-Currency` header: ```bash curl https://api.paylinks.ro/api/v1/paylinks \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "X-Currency: EUR" ``` Supported values: `RON` (default), `EUR`, `GBP`. This header affects which currency is used when creating payment links and how monetary values are returned. ## Token Lifecycle | Property | Value | |----------|-------| | Type | JWT (JSON Web Token) | | Expiry (magic link / Google) | 7 days | | Expiry (API key exchange) | 1 hour | | Algorithm | HS256 | | Revocation | Bump user's `tokenVersion` to invalidate all tokens | Tokens are stateless. There is no refresh token flow — request a new magic link or re-authenticate via Google OAuth when the token expires (or exchange API key credentials again for integration flows). ## Error Responses Missing or invalid token: ```json { "error": { "message": "Unauthorized", "code": "UNAUTHORIZED" } } ``` HTTP status: `401` --- # API Reference Overview ## Base URL ``` https://api.paylinks.ro/api/v1 ``` All endpoints are relative to this base URL. ## Authentication Protected endpoints require a JWT bearer token: ``` Authorization: Bearer ``` See [Authentication](/authentication) for how to obtain a token. ## Headers | Header | Required | Description | |--------|----------|-------------| | `Authorization` | Yes (protected) | `Bearer ` | | `Content-Type` | Yes (POST/PUT/PATCH) | `application/json` | | `X-Currency` | No | `RON`, `EUR`, or `GBP` (default: `RON`) | ## Amount Format All monetary amounts are in **minor units** (bani for RON, cents for EUR/GBP): | Display | API Value | Currency | |---------|-----------|----------| | 49.99 RON | `4999` | RON | | 10.00 EUR | `1000` | EUR | | 1.50 GBP | `150` | GBP | 1 RON = 100 bani. Always send and expect integers. ## Currencies PayLinks supports three currencies: - **RON** — Romanian Leu (default) - **EUR** — Euro - **GBP** — British Pound Set the currency via the `X-Currency` header. ## Pagination Most list endpoints use **cursor-based pagination**: ```bash # First page curl "https://api.paylinks.ro/api/v1/paylinks?limit=20" # Next page (use nextCursor from previous response) curl "https://api.paylinks.ro/api/v1/paylinks?limit=20&cursor=eyJpZCI6Ij..." ``` Response format: ```json { "data": [...], "nextCursor": "eyJpZCI6Ij...", "hasMore": true } ``` | Parameter | Type | Default | Max | |-----------|------|---------|-----| | `limit` | integer | 20 | 100 | | `cursor` | string | — | — | Some endpoints (disputes) use offset-based pagination with `limit` and `offset` parameters. ## Error Format All errors follow a consistent format: ```json { "error": { "message": "Human-readable error description", "code": "OPTIONAL_ERROR_CODE" } } ``` ### Common HTTP Status Codes | Status | Meaning | |--------|---------| | `200` | Success | | `201` | Created | | `400` | Bad request / validation error | | `401` | Unauthorized (missing or invalid token) | | `403` | Forbidden (insufficient permissions) | | `404` | Resource not found | | `500` | Internal server error | ## Rate Limiting The API applies rate limiting per IP and per user. If you exceed the limit, you'll receive a `429 Too Many Requests` response. Wait and retry with exponential backoff. ## API Modules | Module | Description | |--------|-------------| | [Auth](/api-reference/auth) | Magic link and Google OAuth | | [PayLinks](/api-reference/paylinks) | Create and manage payment links | | [Transactions](/api-reference/transactions) | Transaction history and refunds | | [Customers](/api-reference/customers) | Customer segments | | [Analytics](/api-reference/analytics) | Revenue and MRR data | | [Stripe](/api-reference/stripe) | Stripe Connect, payouts, balance | | [Disputes](/api-reference/disputes) | Payment disputes | | [Settings](/api-reference/settings) | Account settings | | [Subscriptions](/api-reference/subscriptions) | Recurring subscriptions | | [Purchases](/api-reference/purchases) | Buyer purchases | | [Email](/api-reference/email) | Broadcasts and subscribers | | [Uploads](/api-reference/uploads) | File uploads | | [Public](/api-reference/public) | Public checkout endpoints | --- # Auth Authentication endpoints. All endpoints in this module are **public** (no token required). ## POST /auth/request Request a magic link email. ```bash curl -X POST https://api.paylinks.ro/api/v1/auth/request \ -H "Content-Type: application/json" \ -d '{"email": "user@example.com","redirectTo":"https://app.paylinks.ro/auth/callback"}' ``` **Request Body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `email` | string | Yes | Email address | | `redirectTo` | string (URL) | No | Frontend callback URL (same origin as app) | **Response (200):** ```json {"ok": true} ``` --- ## GET /auth/verify Verify a magic link token and receive a JWT. ```bash curl "https://api.paylinks.ro/api/v1/auth/verify?token=abc123" ``` **Query Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `token` | string | Yes | Magic link token from email | | `redirectTo` | string (URL) | No | Frontend callback URL (same origin as app) | **Response (200):** ```json { "token": "eyJhbGciOiJIUzI1NiIs...", "needsOnboarding": false } ``` --- ## GET /auth/google/start Start Google OAuth flow. Redirects to Google consent screen. ```bash curl -L "https://api.paylinks.ro/api/v1/auth/google/start?redirectTo=https://yourapp.com/callback" ``` **Query Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `redirectTo` | string | No | URL to redirect after auth | **Response:** `302` redirect to Google. --- ## GET /auth/google/callback Google OAuth callback. Called by Google after user consents. **Query Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `code` | string | Yes | OAuth authorization code | | `state` | string | Yes | OAuth state parameter | **Response (200):** ```json { "token": "eyJhbGciOiJIUzI1NiIs...", "needsOnboarding": false, "user": { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "user@gmail.com", "role": "USER" } } ``` --- ## POST /auth/google/onetap Authenticate via Google One Tap. ```bash curl -X POST https://api.paylinks.ro/api/v1/auth/google/onetap \ -H "Content-Type: application/json" \ -d '{"credential": "GOOGLE_JWT_CREDENTIAL"}' ``` **Request Body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `credential` | string | Yes | Google One Tap JWT | **Response (200):** ```json { "token": "eyJhbGciOiJIUzI1NiIs...", "user": { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "user@gmail.com", "role": "USER" } } ``` --- # PayLinks Create and manage payment links. All endpoints require authentication. ## GET /paylinks List your payment links. ```bash curl https://api.paylinks.ro/api/v1/paylinks \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "X-Currency: RON" ``` **Query Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `limit` | integer | 20 | Items per page (max 100) | | `cursor` | string | — | Pagination cursor | **Response (200):** ```json { "data": [ { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "title": "Premium Course", "slug": "premium-course", "priceType": "FIXED", "serviceType": "DIGITAL_PRODUCT", "amount": 4999, "currency": "RON", "active": true, "totalRevenue": 149970, "totalTransactions": 30, "createdAt": "2026-01-15T10:00:00.000Z" } ], "nextCursor": "eyJpZCI6Ij...", "hasMore": true } ``` --- ## POST /paylinks Create a new payment link. ```bash curl -X POST https://api.paylinks.ro/api/v1/paylinks \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -H "X-Currency: RON" \ -d '{ "title": "Premium Course", "description": "Full access to all modules", "priceType": "FIXED", "serviceType": "DIGITAL_PRODUCT", "amount": 4999, "collectEmail": true, "collectName": true }' ``` **Request Body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `title` | string | Yes | Payment link title | | `description` | string | No | Description shown to buyers | | `priceType` | string | Yes | `FIXED`, `PAY_WHAT_YOU_WANT`, or `FREE` | | `serviceType` | string | Yes | `SERVICE`, `DIGITAL_PRODUCT`, or `SUBSCRIPTION` | | `amount` | integer | Yes (FIXED) | Price in minor units | | `currency` | string | No | `RON`, `EUR`, `GBP` | | `minAmount` | integer | No | Minimum for pay-what-you-want | | `collectEmail` | boolean | No | Collect buyer email (default: true) | | `collectName` | boolean | No | Collect buyer name | | `collectPhone` | boolean | No | Collect buyer phone | | `collectAddress` | boolean | No | Collect buyer address | | `requireQuantity` | boolean | No | Allow quantity selection | | `maxQuantity` | integer | No | Max quantity per purchase | | `successUrl` | string | No | Redirect URL after payment | | `cancelUrl` | string | No | Redirect URL on cancel | | `imageUrl` | string | No | Product image URL | | `fileUrl` | string | No | Digital product download URL | | `thankYouMessage` | string | No | Custom thank you message | | `recurringInterval` | string | No | `month` or `year` (for SUBSCRIPTION) | | `trialDays` | integer | No | Free trial days (for SUBSCRIPTION) | | `active` | boolean | No | Whether link is live (default: true) | **Response (201):** ```json { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "title": "Premium Course", "slug": "premium-course", "priceType": "FIXED", "serviceType": "DIGITAL_PRODUCT", "amount": 4999, "currency": "RON", "active": true, "collectEmail": true, "collectName": true, "createdAt": "2026-02-17T10:00:00.000Z" } ``` --- ## POST /paylinks/ai-draft Generate a paylink using AI from a text prompt. ```bash curl -X POST https://api.paylinks.ro/api/v1/paylinks/ai-draft \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"prompt": "A cooking course for beginners, 99 RON"}' ``` **Request Body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `prompt` | string | Yes | Natural language description | **Response (200):** Returns a PayLink object with AI-generated fields. --- ## PATCH /paylinks/:id Update an existing paylink. ```bash curl -X PATCH https://api.paylinks.ro/api/v1/paylinks/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"title": "Updated Course Title", "amount": 5999}' ``` All fields from the create endpoint are accepted (except `priceType` and `serviceType`). **Response (200):** Updated PayLink object. --- ## POST /paylinks/:id/duplicate Create a copy of an existing paylink. ```bash curl -X POST https://api.paylinks.ro/api/v1/paylinks/a1b2c3d4-e5f6-7890-abcd-ef1234567890/duplicate \ -H "Authorization: Bearer YOUR_TOKEN" ``` **Response (201):** New PayLink object (copy of the original with a new slug). --- ## DELETE /paylinks/:id Delete a paylink. ```bash curl -X DELETE https://api.paylinks.ro/api/v1/paylinks/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \ -H "Authorization: Bearer YOUR_TOKEN" ``` **Response (200):** ```json {"message": "PayLink deleted"} ``` --- # Transactions Transaction history and refunds. All endpoints require authentication. ## GET /transactions List transactions for the authenticated user. ```bash curl "https://api.paylinks.ro/api/v1/transactions?limit=20&status=COMPLETED" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "X-Currency: RON" ``` **Query Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `limit` | integer | 20 | Items per page (max 100) | | `cursor` | string | — | Pagination cursor | | `status` | string | — | Filter: `PENDING`, `COMPLETED`, `REFUNDED`, `FAILED`, `DISPUTED` | **Response (200):** ```json { "data": [ { "id": "tx-001", "payLinkId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "amount": 4999, "currency": "RON", "status": "COMPLETED", "customerEmail": "buyer@example.com", "customerName": "Ion Popescu", "stripePaymentIntentId": "pi_1234567890", "platformFee": 200, "netAmount": 4799, "createdAt": "2026-02-16T14:30:00.000Z", "payLink": { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "title": "Premium Course", "slug": "premium-course" } } ], "nextCursor": "eyJpZCI6Ij...", "hasMore": false } ``` ### Transaction Fields | Field | Type | Description | |-------|------|-------------| | `amount` | integer | Total amount in minor units | | `platformFee` | integer | PayLinks platform fee | | `netAmount` | integer | Amount after platform fee | | `status` | string | `PENDING`, `COMPLETED`, `REFUNDED`, `FAILED`, `DISPUTED` | --- # Customers Customer data segmented by behavior. Requires authentication. ## GET /customers List customers, optionally filtered by segment. ```bash curl "https://api.paylinks.ro/api/v1/customers?segment=top&limit=10" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "X-Currency: RON" ``` **Query Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `limit` | integer | 20 | Items per page (max 100) | | `cursor` | string | — | Pagination cursor | | `segment` | string | — | Filter: `new`, `returning`, `top` | ### Customer Segments | Segment | Description | |---------|-------------| | `new` | First-time buyers | | `returning` | Customers with 2+ purchases | | `top` | Highest spenders | **Response (200):** ```json { "data": [ { "email": "buyer@example.com", "name": "Ion Popescu", "totalSpent": 14997, "transactionCount": 3, "lastTransactionAt": "2026-02-16T14:30:00.000Z", "segment": "returning" } ], "nextCursor": "eyJpZCI6Ij...", "hasMore": false } ``` ### Customer Fields | Field | Type | Description | |-------|------|-------------| | `totalSpent` | integer | Total spent in minor units | | `transactionCount` | integer | Number of completed transactions | | `segment` | string | Computed segment: `new`, `returning`, `top` | --- # Analytics Revenue, MRR, and payment analytics. All endpoints require authentication. ## GET /analytics/summary Get an aggregated dashboard summary for a date range. ```bash curl "https://api.paylinks.ro/api/v1/analytics/summary?from=2026-01-01&to=2026-02-17" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "X-Currency: RON" ``` **Query Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `from` | date | Start date (ISO 8601) | | `to` | date | End date (ISO 8601) | **Response (200):** ```json { "totalRevenue": 499900, "totalTransactions": 100, "totalCustomers": 85, "averageOrderValue": 4999, "periodRevenue": 149970, "periodTransactions": 30 } ``` All amounts are in minor units. --- ## GET /analytics/revenue Get revenue data over time. ```bash curl "https://api.paylinks.ro/api/v1/analytics/revenue?from=2026-01-01&to=2026-02-17" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "X-Currency: RON" ``` **Response (200):** ```json { "data": [ {"date": "2026-01-01", "revenue": 14999}, {"date": "2026-01-02", "revenue": 9998}, {"date": "2026-01-03", "revenue": 24997} ] } ``` --- ## GET /analytics/mrr Get monthly recurring revenue data. ```bash curl https://api.paylinks.ro/api/v1/analytics/mrr \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "X-Currency: RON" ``` **Response (200):** ```json { "mrr": 49990, "subscribers": 10 } ``` --- ## GET /analytics/payment-methods Get payment method usage breakdown. ```bash curl https://api.paylinks.ro/api/v1/analytics/payment-methods \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "X-Currency: RON" ``` **Response (200):** ```json { "data": [ {"method": "card", "count": 85, "total": 424915}, {"method": "apple_pay", "count": 10, "total": 49990}, {"method": "google_pay", "count": 5, "total": 24995} ] } ``` --- ## POST /analytics/reconcile Trigger a reconciliation of analytics data with Stripe. ```bash curl -X POST https://api.paylinks.ro/api/v1/analytics/reconcile \ -H "Authorization: Bearer YOUR_TOKEN" ``` **Response (200):** ```json {"message": "Reconciliation started"} ``` --- # Stripe Stripe Connect account management and payouts. All endpoints (except `/stripe/pk`) require authentication. ## GET /stripe/balance Get the connected Stripe account balance. ```bash curl https://api.paylinks.ro/api/v1/stripe/balance \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "X-Currency: RON" ``` **Response (200):** ```json { "available": [ {"currency": "ron", "amount": 499900}, {"currency": "eur", "amount": 100000} ], "pending": [ {"currency": "ron", "amount": 49990} ] } ``` --- ## GET /stripe/payouts List payouts for the connected account. ```bash curl "https://api.paylinks.ro/api/v1/stripe/payouts?limit=10" \ -H "Authorization: Bearer YOUR_TOKEN" ``` **Query Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `limit` | integer | 10 | Number of payouts | | `startingAfter` | string | — | Stripe payout ID for pagination | **Response (200):** ```json { "data": [ { "id": "po_1234567890", "amount": 100000, "currency": "ron", "status": "paid", "arrivalDate": "2026-02-18T00:00:00.000Z" } ], "hasMore": false } ``` --- ## POST /stripe/payouts Create a manual payout. ```bash curl -X POST https://api.paylinks.ro/api/v1/stripe/payouts \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"currency": "RON"}' ``` **Request Body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `currency` | string | No | `RON`, `EUR`, or `GBP` | | `statementDescriptor` | string | No | Statement descriptor | **Response (200):** ```json { "id": "po_1234567890", "amount": 499900, "currency": "ron", "status": "pending" } ``` --- ## GET /stripe/accounts/me Get the user's connected Stripe account. ```bash curl https://api.paylinks.ro/api/v1/stripe/accounts/me \ -H "Authorization: Bearer YOUR_TOKEN" ``` **Response (200):** ```json { "id": "acct_1234567890", "chargesEnabled": true, "payoutsEnabled": true, "detailsSubmitted": true } ``` --- ## GET /stripe/accounts/me/requirements Get pending Stripe verification requirements. ```bash curl https://api.paylinks.ro/api/v1/stripe/accounts/me/requirements \ -H "Authorization: Bearer YOUR_TOKEN" ``` **Response (200):** ```json { "currentlyDue": [], "pastDue": [], "eventuallyDue": ["individual.verification.document"] } ``` --- ## POST /stripe/account-link Create a Stripe account link for onboarding or updating details. ```bash curl -X POST https://api.paylinks.ro/api/v1/stripe/account-link \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "refreshUrl": "https://paylinks.ro/dashboard/settings", "returnUrl": "https://paylinks.ro/dashboard/settings" }' ``` **Response (200):** ```json {"url": "https://connect.stripe.com/setup/..."} ``` --- ## GET /stripe/pk Get the Stripe publishable key. **Public endpoint** (no auth required). ```bash curl https://api.paylinks.ro/api/v1/stripe/pk ``` **Response (200):** ```json {"publishableKey": "pk_live_..."} ``` --- # Disputes Payment dispute management. All endpoints require authentication. ## GET /disputes List disputes for the authenticated user. ```bash curl "https://api.paylinks.ro/api/v1/disputes?status=OPEN&limit=20" \ -H "Authorization: Bearer YOUR_TOKEN" ``` **Query Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `limit` | integer | 20 | Items per page | | `offset` | integer | 0 | Pagination offset | | `status` | string | — | Filter: `OPEN`, `UNDER_REVIEW`, `WON`, `LOST` | **Response (200):** ```json { "data": [ { "id": "disp-001", "transactionId": "tx-001", "stripeDisputeId": "dp_1234567890", "amount": 4999, "currency": "RON", "status": "OPEN", "reason": "fraudulent", "dueBy": "2026-03-01T00:00:00.000Z", "createdAt": "2026-02-15T10:00:00.000Z" } ], "total": 1 } ``` ### Dispute Statuses | Status | Description | |--------|-------------| | `OPEN` | New dispute, evidence can be submitted | | `UNDER_REVIEW` | Evidence submitted, awaiting Stripe decision | | `WON` | Dispute resolved in seller's favor | | `LOST` | Dispute resolved in buyer's favor | --- ## GET /disputes/:id Get a single dispute by ID. ```bash curl https://api.paylinks.ro/api/v1/disputes/disp-001 \ -H "Authorization: Bearer YOUR_TOKEN" ``` **Response (200):** Full dispute object. --- ## POST /disputes/:id/evidence Submit evidence for a dispute. ```bash curl -X POST https://api.paylinks.ro/api/v1/disputes/disp-001/evidence \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "uncategorizedText": "Customer received the digital product on Feb 15. Download logs attached.", "uncategorizedFile": "https://files.paylinks.ro/evidence/receipt.pdf" }' ``` **Request Body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `uncategorizedText` | string | No | Text evidence | | `uncategorizedFile` | string | No | URL to evidence file | **Response (200):** ```json {"message": "Evidence submitted"} ``` --- # Settings Account settings management. All endpoints require authentication. ## GET /settings/me Get the current user's settings. ```bash curl https://api.paylinks.ro/api/v1/settings/me \ -H "Authorization: Bearer YOUR_TOKEN" ``` **Response (200):** ```json { "userId": "550e8400-e29b-41d4-a716-446655440000", "autoPayouts": false, "payoutInterval": "weekly", "defaultCurrency": "RON", "emailNotifications": true, "disputeNotifications": true } ``` --- ## PUT /settings/me Update account settings. ```bash curl -X PUT https://api.paylinks.ro/api/v1/settings/me \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "autoPayouts": true, "payoutInterval": "daily", "defaultCurrency": "EUR", "emailNotifications": true, "disputeNotifications": true }' ``` **Request Body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `autoPayouts` | boolean | No | Enable automatic payouts | | `payoutInterval` | string | No | `daily`, `weekly`, or `monthly` | | `defaultCurrency` | string | No | `RON`, `EUR`, or `GBP` | | `emailNotifications` | boolean | No | Receive email notifications | | `disputeNotifications` | boolean | No | Receive dispute notifications | **Response (200):** Updated settings object. --- ## PUT /settings/me/name Update display name. ```bash curl -X PUT https://api.paylinks.ro/api/v1/settings/me/name \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "Andrei Bucur"}' ``` **Response (200):** ```json {"name": "Andrei Bucur"} ``` --- # Subscriptions View subscriptions tied to your paylinks. Requires authentication. ## GET /subscriptions List subscriptions for the seller (subscriptions on your paylinks). ```bash curl "https://api.paylinks.ro/api/v1/subscriptions?limit=20" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "X-Currency: RON" ``` **Query Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `limit` | integer | 20 | Items per page (max 100) | | `cursor` | string | — | Pagination cursor | **Response (200):** ```json { "data": [ { "id": "sub-001", "payLinkId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "customerEmail": "subscriber@example.com", "status": "ACTIVE", "currentPeriodEnd": "2026-03-15T00:00:00.000Z", "amount": 2999, "currency": "RON", "interval": "month", "createdAt": "2026-02-15T10:00:00.000Z" } ], "nextCursor": null, "hasMore": false } ``` ### Subscription Statuses | Status | Description | |--------|-------------| | `ACTIVE` | Subscription is active and billing | | `CANCELED` | Canceled, active until period end | | `PAST_DUE` | Payment failed, retry in progress | --- # Purchases Buyer-side purchase management. Requires authentication. ## GET /purchases List the authenticated user's purchases. ```bash curl "https://api.paylinks.ro/api/v1/purchases?limit=20" \ -H "Authorization: Bearer YOUR_TOKEN" ``` **Query Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `limit` | integer | 20 | Items per page (max 100) | | `cursor` | string | — | Pagination cursor | **Response (200):** ```json { "data": [ { "id": "pur-001", "payLinkId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "transactionId": "tx-001", "customerEmail": "buyer@example.com", "status": "COMPLETED", "payLink": { "title": "Premium Course", "slug": "premium-course", "serviceType": "DIGITAL_PRODUCT" }, "createdAt": "2026-02-16T14:30:00.000Z" } ], "nextCursor": null, "hasMore": false } ``` --- ## POST /purchases/presign Get a presigned URL to download a purchased digital product. ```bash curl -X POST https://api.paylinks.ro/api/v1/purchases/presign \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"purchaseId": "pur-001"}' ``` **Request Body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `purchaseId` | string | Yes | Purchase ID | **Response (200):** ```json { "url": "https://files.paylinks.ro/...", "expiresIn": 3600 } ``` --- ## POST /purchases/cancel-subscription Cancel a subscription as a buyer. ```bash curl -X POST https://api.paylinks.ro/api/v1/purchases/cancel-subscription \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"subscriptionId": "sub-001"}' ``` **Response (200):** ```json {"message": "Subscription canceled. Active until end of current billing period."} ``` --- # Email Email broadcasts and subscriber management. All endpoints require authentication. ## GET /email/broadcasts List all broadcasts. ```bash curl https://api.paylinks.ro/api/v1/email/broadcasts \ -H "Authorization: Bearer YOUR_TOKEN" ``` **Response (200):** ```json [ { "id": "bc-001", "subject": "New Product Launch", "body": "

Exciting news!

...", "status": "SENT", "recipientCount": 150, "sentAt": "2026-02-15T10:00:00.000Z", "createdAt": "2026-02-15T09:00:00.000Z" } ] ``` --- ## POST /email/broadcasts Create a new broadcast (saved as draft). ```bash curl -X POST https://api.paylinks.ro/api/v1/email/broadcasts \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "subject": "New Product Launch", "body": "

Exciting news!

We just launched a new course.

" }' ``` **Request Body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `subject` | string | Yes | Email subject line | | `body` | string | Yes | HTML email body | **Response (201):** Broadcast object with `status: "DRAFT"`. --- ## GET /email/broadcasts/:id Get a single broadcast. ```bash curl https://api.paylinks.ro/api/v1/email/broadcasts/bc-001 \ -H "Authorization: Bearer YOUR_TOKEN" ``` **Response (200):** Full broadcast object. --- ## POST /email/broadcasts/:id/send Send a broadcast to all subscribers. ```bash curl -X POST https://api.paylinks.ro/api/v1/email/broadcasts/bc-001/send \ -H "Authorization: Bearer YOUR_TOKEN" ``` **Response (200):** ```json { "message": "Broadcast queued for sending", "recipientCount": 150 } ``` --- ## POST /email/broadcasts/ai-draft Generate a broadcast using AI. ```bash curl -X POST https://api.paylinks.ro/api/v1/email/broadcasts/ai-draft \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"prompt": "Announce our new subscription plans starting at 29.99 RON/month"}' ``` **Response (200):** Broadcast object with AI-generated subject and body. --- ## GET /email/subscribers List email subscribers. ```bash curl "https://api.paylinks.ro/api/v1/email/subscribers?limit=50" \ -H "Authorization: Bearer YOUR_TOKEN" ``` **Response (200):** ```json { "data": [ { "id": "sub-email-001", "email": "subscriber@example.com", "name": "Maria Ionescu", "subscribedAt": "2026-01-10T10:00:00.000Z" } ], "nextCursor": null, "hasMore": false } ``` --- ## POST /email/subscribers Add a single subscriber. ```bash curl -X POST https://api.paylinks.ro/api/v1/email/subscribers \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"email": "new@example.com", "name": "Ion"}' ``` **Response (201):** ```json {"id": "sub-email-002", "email": "new@example.com"} ``` --- ## POST /email/subscribers/bulk Add multiple subscribers at once. ```bash curl -X POST https://api.paylinks.ro/api/v1/email/subscribers/bulk \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "subscribers": [ {"email": "a@example.com", "name": "Alice"}, {"email": "b@example.com", "name": "Bob"} ] }' ``` **Response (200):** ```json {"added": 2, "skipped": 0} ``` The `skipped` count represents subscribers that already exist. --- # Uploads File upload via presigned URLs (Cloudflare R2). Requires authentication. ## POST /uploads/presign Get a presigned URL to upload a file. ```bash curl -X POST https://api.paylinks.ro/api/v1/uploads/presign \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"filename": "product.pdf", "contentType": "application/pdf"}' ``` **Request Body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `filename` | string | Yes | File name | | `contentType` | string | Yes | MIME type (e.g., `application/pdf`, `image/png`) | **Response (200):** ```json { "uploadUrl": "https://r2.paylinks.ro/presigned-upload-url...", "fileUrl": "https://files.paylinks.ro/uploads/abc123/product.pdf" } ``` ### Upload Flow 1. Call `POST /uploads/presign` to get a presigned URL 2. `PUT` the file contents to the `uploadUrl` 3. Use the `fileUrl` in paylink fields (`imageUrl`, `fileUrl`) ```bash # Step 2: Upload the file curl -X PUT "https://r2.paylinks.ro/presigned-upload-url..." \ -H "Content-Type: application/pdf" \ --data-binary @product.pdf ``` --- # Public Checkout Public endpoints for the checkout flow. **No authentication required.** These endpoints power the public checkout page at `https://paylinks.ro/p/{slug}`. ## GET /paylinks/public List active public paylinks for sitemap/indexing. ```bash curl https://api.paylinks.ro/api/v1/paylinks/public ``` **Response (200):** ```json { "items": [ { "slug": "premium-course", "updatedAt": "2026-02-17T10:00:00.000Z" } ] } ``` --- ## GET /paylinks/public/:slug Get a single public paylink by slug. ```bash curl https://api.paylinks.ro/api/v1/paylinks/public/premium-course ``` **Response (200):** Full public paylink object. **Response (404):** PayLink not found. --- ## POST /paylinks/public/:slug/payment-intents Create a Stripe payment intent for checkout. ```bash curl -X POST https://api.paylinks.ro/api/v1/paylinks/public/premium-course/payment-intents \ -H "Content-Type: application/json" \ -d '{ "email": "buyer@example.com", "phone": "+40712345678", "amount": 49.99 }' ``` **Request Body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `email` | string | No | Buyer's email (required if paylink collects email) | | `phone` | string | No | Buyer's phone | | `amount` | number | No | Amount in major units (required for `FLEXIBLE`) | | `address` | object | No | Billing address fields | **Response (200):** ```json { "client_secret": "pi_1234567890_secret_abc123", "connectedAccountId": "acct_1234567890" } ``` Use `client_secret` with Stripe.js to confirm the payment. --- ## POST /paylinks/public/:slug/leads Claim a free paylink (no payment required). ```bash curl -X POST https://api.paylinks.ro/api/v1/paylinks/public/free-ebook/leads \ -H "Content-Type: application/json" \ -d '{"email": "reader@example.com", "name": "Maria"}' ``` **Request Body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `email` | string | No | Email address (email or phone is required) | | `phone` | string | No | Phone number (email or phone is required) | | `name` | string | No | Name | **Response (201):** ```json {"success": true} ``` --- ## POST /paylinks/public/:slug/subscriptions Create or confirm a subscription for a recurring paylink. ```bash curl -X POST https://api.paylinks.ro/api/v1/paylinks/public/monthly-plan/subscriptions \ -H "Content-Type: application/json" \ -d '{ "email": "subscriber@example.com", "name": "Ion", "paymentMethodId": "pm_1234567890" }' ``` **Request Body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `email` | string | No | Subscriber's email (required if paylink collects email) | | `name` | string | No | Subscriber's name | | `paymentMethodId` | string | No | Stripe payment method ID (step 2) | | `customerId` | string | No | Existing Stripe customer ID from step 1 | | `phone` | string | No | Subscriber phone | | `address` | object | No | Billing address fields | **Response (200, step 1):** ```json { "step": "setup", "client_secret": "seti_1234567890_secret_abc123", "customerId": "cus_1234567890", "connectedAccountId": "acct_1234567890" } ``` **Response (200, step 2):** ```json { "step": "complete", "subscriptionId": "sub_1234567890", "status": "active", "client_secret": null, "paymentIntentStatus": null, "connectedAccountId": "acct_1234567890" } ``` --- # WebMCP Integration WebMCP enables browser-based AI agents to interact with PayLinks directly from the dashboard. ## What is WebMCP? WebMCP (Web Model Context Protocol) is a browser-native protocol that exposes structured tools to AI agents running in Chrome. When enabled, AI assistants can manage your payment links, view analytics, send emails, and more — all through natural language. ## How to Enable WebMCP is an experimental Chrome feature. To enable it: 1. Open Chrome and navigate to `chrome://flags/#enable-webmcp-testing` 2. Set the flag to **Enabled** 3. Restart Chrome 4. Visit [paylinks.ro/dashboard](https://paylinks.ro/dashboard) The dashboard automatically exposes WebMCP tools when the flag is enabled. ## Available Tools PayLinks exposes **36 WebMCP tools** organized by module: ### Payment Links | Tool | Description | |------|-------------| | `list_paylinks` | List all payment links | | `get_paylink` | Get a specific paylink by ID | | `create_paylink` | Create a new payment link | | `update_paylink` | Update an existing paylink | | `delete_paylink` | Delete a paylink | | `duplicate_paylink` | Duplicate a paylink | | `ai_draft_paylink` | AI-generate a paylink from prompt | ### Transactions | Tool | Description | |------|-------------| | `list_transactions` | List transactions with filters | ### Customers | Tool | Description | |------|-------------| | `list_customers` | List customers by segment | ### Analytics | Tool | Description | |------|-------------| | `get_analytics_summary` | Dashboard summary | | `get_revenue_data` | Revenue over time | | `get_mrr_data` | Monthly recurring revenue | | `get_payment_methods` | Payment method breakdown | | `trigger_reconciliation` | Reconcile with Stripe | ### Stripe | Tool | Description | |------|-------------| | `get_stripe_balance` | Account balance | | `list_stripe_payouts` | List payouts | | `create_stripe_payout` | Create a payout | | `get_stripe_account` | Connected account details | | `get_stripe_requirements` | Verification requirements | | `create_stripe_account_link` | Onboarding link | ### Disputes | Tool | Description | |------|-------------| | `list_disputes` | List disputes | | `get_dispute` | Get dispute details | | `submit_dispute_evidence` | Submit evidence | ### Settings | Tool | Description | |------|-------------| | `get_settings` | Get account settings | | `update_settings` | Update settings | ### Subscriptions & Purchases | Tool | Description | |------|-------------| | `list_subscriptions` | List seller subscriptions | | `list_purchases` | List buyer purchases | | `download_purchase` | Get download URL | | `cancel_subscription` | Cancel a subscription | ### Email | Tool | Description | |------|-------------| | `list_broadcasts` | List email broadcasts | | `create_broadcast` | Create a broadcast | | `send_broadcast` | Send a broadcast | | `ai_draft_broadcast` | AI-draft a broadcast | | `list_subscribers` | List email subscribers | | `add_subscriber` | Add a subscriber | ## Example Usage With a WebMCP-compatible AI agent in Chrome: > "Create a payment link for a React course at 149 RON" The agent will call `create_paylink` with the appropriate parameters and confirm the result. > "Show me my revenue for the last 30 days" The agent calls `get_analytics_summary` and `get_revenue_data` to display your analytics. > "Send an email broadcast announcing the new course to all subscribers" The agent calls `ai_draft_broadcast`, shows you the draft, and then `send_broadcast` when confirmed. ## Security WebMCP tools inherit the user's authenticated session. The AI agent can only perform actions that the logged-in user is authorized to do. No additional API tokens are needed. --- # MCP Server Connect PayLinks to Claude Desktop, Cursor, and other MCP-compatible AI tools. ## What is MCP? The Model Context Protocol (MCP) is a standard for connecting AI assistants to external tools and data sources. The PayLinks MCP server lets you manage your payments through Claude Desktop and other MCP clients. ## Installation ```bash npx mcp-paylinks ``` No global install needed — `npx` runs it directly. ## Configuration ### Environment Variables | Variable | Required | Description | |----------|----------|-------------| | `PAYLINKS_API_TOKEN` | Yes | Your JWT bearer token | | `PAYLINKS_API_BASE_URL` | No | API base URL (default: `https://api.paylinks.ro/api/v1`) | ### Claude Desktop Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS): ```json { "mcpServers": { "paylinks": { "command": "npx", "args": ["mcp-paylinks"], "env": { "PAYLINKS_API_TOKEN": "your-jwt-token-here" } } } } ``` Restart Claude Desktop after saving. ### Cursor Add to `.cursor/mcp.json` in your project: ```json { "mcpServers": { "paylinks": { "command": "npx", "args": ["mcp-paylinks"], "env": { "PAYLINKS_API_TOKEN": "your-jwt-token-here" } } } } ``` ### SSE Transport For remote connections, the MCP server supports SSE (Server-Sent Events) transport: ```bash PAYLINKS_API_TOKEN=your-token npx mcp-paylinks --sse --port 3100 ``` Then connect your MCP client to `http://localhost:3100/sse`. ## Available Tools The MCP server exposes **61 public tools** across **16 modules**: ### PayLinks (8) - `list_paylinks` - `get_paylink` - `create_paylink` - `update_paylink` - `delete_paylink` - `duplicate_paylink` - `toggle_paylink` - `ai_draft_paylink` ### Transactions (1) - `get_transactions` ### Analytics (5) - `get_analytics_summary` - `get_revenue` - `get_mrr` - `get_payment_methods` - `reconcile_analytics` ### Stripe (6) - `get_balance` - `get_payouts` - `create_payout` - `get_stripe_account` - `create_account_link` - `get_account_requirements` ### Customers (1) - `get_customers` ### Subscriptions (1) - `list_subscriptions` ### Disputes (3) - `list_disputes` - `get_dispute` - `submit_dispute_evidence` ### Email (7) - `list_broadcasts` - `create_broadcast` - `get_broadcast` - `send_broadcast` - `ai_draft_broadcast` - `list_subscribers` - `add_subscriber` ### Purchases (3) - `list_purchases` - `presign_download` - `cancel_purchase_subscription` ### Profile (3) - `get_me` - `update_me` - `complete_onboarding` ### Settings (2) - `get_settings` - `update_settings` ### Auth (2) - `request_magic_link` - `verify_magic_link` ### Uploads (1) - `presign_upload` ### Crypto (3) - `crypto_list_wallets` - `crypto_get_balance` - `crypto_create_payment_link` ### Public (3) - `list_public_paylinks` - `get_public_paylink` - `claim_free_paylink` ### Free Tools (12) - `free_generate_proforma_totals` - `free_convert_bank_statement_rows` - `free_verify_cui` - `free_generate_qr_payload` - `free_calculate_pfa_taxes` - `free_calculate_vat` - `free_calculate_net_income` - `free_compare_payment_commissions` - `free_calculate_hourly_rate` - `free_get_fiscal_calendar_events` - `free_quiz_pfa_vs_srl` - `free_split_bill` ## Available Resources The MCP server also exposes **3 resources**: - `paylinks://dashboard/stats` - `paylinks://activity/recent` - `paylinks://account/overview` ## Privileged Tools Privileged MCP operations are intentionally omitted from the public catalog and are registered only for elevated tokens. ## Example Conversations **With Claude Desktop:** > "Show me my PayLinks dashboard summary" Claude calls `get_analytics_summary` and presents your revenue, transactions, and customer count. > "Create a payment link for consulting at 500 RON per hour" Claude calls `create_paylink` with the right parameters. > "Draft and send an email to all subscribers about our Black Friday sale" Claude calls `ai_draft_broadcast`, shows the draft, and on approval calls `send_broadcast`. ## Getting Your Token 1. Go to [paylinks.ro](https://paylinks.ro) and sign in 2. Open browser DevTools (F12) 3. Go to Application > Local Storage > `https://paylinks.ro` 4. Copy the `token` value Or use the magic link flow programmatically (see [Authentication](/authentication)). --- # ChatGPT Actions Integrate PayLinks with ChatGPT using OpenAI's Actions (formerly Plugins). ## Overview ChatGPT Actions allow custom GPTs to interact with the PayLinks API. This integration lets ChatGPT users manage payment links and view analytics through natural conversation. ## Setup ### 1. Create a Custom GPT 1. Go to [chat.openai.com](https://chat.openai.com) and click "Explore GPTs" > "Create" 2. In the **Configure** tab, scroll to **Actions** 3. Click **Create new action** ### 2. Import the OpenAPI Spec PayLinks provides a public OpenAPI spec for ChatGPT Actions: ``` https://paylinks.ro/openapi-public.yaml ``` Paste this URL in the **Import from URL** field. ### 3. Configure Authentication 1. Set **Authentication type** to "API Key" 2. Set **Auth Type** to "Bearer" 3. Enter your PayLinks JWT token as the API key ### 4. Test the Action Try asking your custom GPT: - "List my payment links" - "Create a payment link for a workshop at 199 RON" - "Show my revenue summary" ## Available Endpoints The public OpenAPI spec exposes a subset of the PayLinks API suitable for ChatGPT: - Payment link CRUD operations - Transaction listing - Analytics summary - Customer listing Sensitive and privileged endpoints are excluded for security. ## Limitations - ChatGPT Actions have a 100,000 character response limit - Complex multi-step operations may require multiple turns - File uploads are not supported through Actions - Token refresh requires manual intervention when expired --- # Webhooks PayLinks uses Stripe webhooks to process payment events in real time. ## Webhook URL ``` https://paylinks.ro/webhooks/stripe ``` This endpoint is registered with Stripe and receives all relevant events for connected accounts. ## Event Types PayLinks processes the following Stripe webhook events: ### Payment Events | Event | Description | |-------|-------------| | `payment_intent.succeeded` | Payment completed successfully. Creates a transaction record. | | `payment_intent.payment_failed` | Payment failed. Updates transaction status to FAILED. | ### Subscription Events | Event | Description | |-------|-------------| | `customer.subscription.created` | New subscription started. | | `customer.subscription.updated` | Subscription status changed (e.g., active, past_due). | | `customer.subscription.deleted` | Subscription canceled and ended. | | `invoice.payment_succeeded` | Recurring payment collected successfully. | | `invoice.payment_failed` | Recurring payment failed. | ### Dispute Events | Event | Description | |-------|-------------| | `charge.dispute.created` | New dispute opened by cardholder. | | `charge.dispute.updated` | Dispute status changed. | | `charge.dispute.closed` | Dispute resolved (won or lost). | ### Payout Events | Event | Description | |-------|-------------| | `payout.paid` | Payout arrived in bank account. | | `payout.failed` | Payout failed. | ### Account Events | Event | Description | |-------|-------------| | `account.updated` | Connected account details changed (verification, capabilities). | ## Signature Verification All webhook events are verified using Stripe's webhook signature: ``` Stripe-Signature: t=timestamp,v1=signature ``` The webhook handler verifies the signature against the webhook secret before processing any event. Events with invalid signatures are rejected with a `400` status. ## Event Processing Events are processed synchronously for critical paths (payment success, dispute creation) and asynchronously via pg-boss workers for non-critical operations (analytics updates, email notifications). ### Idempotency All webhook handlers are idempotent. Processing the same event multiple times produces the same result. Stripe may deliver events more than once, and this is handled gracefully. ## Testing Webhooks Locally Use the Stripe CLI to forward webhooks to your local development server: ```bash stripe listen --forward-to localhost:4000/webhooks/stripe ``` The CLI prints a webhook signing secret — set it as `STRIPE_WEBHOOK_SECRET` in your `.env`. --- # Crypto Payments (Coming Soon) Cryptocurrency payment support is on the PayLinks roadmap. ## Planned Features - Accept crypto payments alongside traditional card payments - Support for major cryptocurrencies (BTC, ETH, USDT, USDC) - Automatic conversion to fiat (RON, EUR, GBP) - Crypto-native checkout experience - On-chain payment verification ## How It Will Work 1. Sellers enable crypto payments in their settings 2. Buyers see a "Pay with Crypto" option on the checkout page 3. A wallet address and amount are generated per transaction 4. Payment is verified on-chain and the transaction is completed 5. Funds are converted to fiat and added to the seller's balance ## Timeline This feature is in the planning phase. Follow [@AiPaylinks](https://x.com/AiPaylinks) on X for updates. --- # Crypto Wallets (Coming Soon) Built-in wallet management for crypto payments. ## Planned Features - **Custodial wallets** — Auto-generated wallets per user for receiving crypto payments - **Multi-chain support** — Ethereum, Bitcoin, and Solana networks - **Balance tracking** — Real-time balance across all supported chains - **Withdrawal** — Send crypto to external wallets - **Conversion** — One-click convert crypto balance to RON/EUR/GBP ## Planned API Endpoints ``` GET /crypto/wallets — List user wallets POST /crypto/wallets — Create a new wallet GET /crypto/wallets/:id — Get wallet details and balance POST /crypto/wallets/withdraw — Withdraw to external address ``` These endpoints are not yet available. This page will be updated when the feature launches. --- # Crypto Payments Processing (Coming Soon) Accept cryptocurrency payments through PayLinks. ## Planned Features - **Checkout integration** — Crypto option appears alongside card payments - **QR code payments** — Scan-to-pay for mobile wallets - **Amount locking** — Lock the crypto amount at checkout to avoid volatility - **Confirmation tracking** — Real-time on-chain confirmation status - **Auto-settlement** — Automatic conversion to fiat at time of payment ## Supported Currencies (Planned) | Currency | Network | |----------|---------| | BTC | Bitcoin | | ETH | Ethereum | | USDT | Ethereum, Tron | | USDC | Ethereum, Solana | | SOL | Solana | ## Planned API Endpoints ``` POST /crypto/payment-intents — Create crypto payment intent GET /crypto/payment-intents/:id — Get payment status POST /crypto/payment-intents/:id/verify — Verify on-chain payment ``` These endpoints are not yet available. Follow [@AiPaylinks](https://x.com/AiPaylinks) for launch updates.