Convilyn Payment API (1.1.0)

Download OpenAPI specification:

Convilyn Support: support@convilyn.com

Payment and Subscription Management API for Convilyn.

Overview

This API handles all payment-related functionality:

  • Plan discovery and pricing
  • Lead management (Pay-Before-Signup flow)
  • Subscription lifecycle (create, cancel, pause, resume)
  • Invoice history
  • Payment method management
  • Usage tracking

Payment Provider

Convilyn uses Paddle as the payment processor.

  • Paddle handles PCI compliance
  • Webhooks sync subscription state
  • Customer Portal for self-service management

Naming Convention

  • Request body fields: camelCase (priceId, leadId, targetBillingCycle)
  • Response body fields (PaymentMethod/Plans): camelCase (hasPaymentMethod, cardBrand) — Pydantic with serialization_alias
  • Response body fields (Subscription/Invoice/Lead): snake_case (has_subscription, invoice_number, payment_state) — raw dict or Pydantic without alias
  • Query params: snake_case (transaction_id)
  • Path params: snake_case (lead_id, transaction_id)

Authentication

Most endpoints require Bearer token authentication. Public endpoints: /plans, /validate-email, /leads, /status/{lead_id}, /activate

Plans

Plan discovery and pricing

Get available payment plans with pricing

Returns available subscription plans with pricing, features, and promotion info.

V1.2 Changes:

  • Added pricing object with monthly/yearly prices
  • Added promotion object for active promotions
  • Added business tier support

Security: Public endpoint (no auth required).

Pricing Source: Backend billing.py is the single source of truth. Frontend should use this API to display prices dynamically.

header Parameters
Accept-Language
string
Default: en

Locale for plan descriptions (en, zh-TW, ja, ko)

Responses

Response samples

Content type
application/json
{
  • "plans": [
    ],
  • "promotion": {
    }
}

Leads

Pay-Before-Signup flow

Validate email before checkout (UX hint)

Pre-payment email validation to prevent users from paying for a subscription when their email is already registered.

Important: This is a UX hint only - non-authoritative. The authoritative check happens in /payment/activate.

Use Case: Checkout page validates email as user types, showing "Sign in instead" link if email is already registered.

Security: Public endpoint (no auth required). Rate limiting should be applied at infrastructure level.

Request Body schema: application/json
required
email
required
string <email> [ 5 .. 254 ] characters

Email to validate before checkout

Responses

Request samples

Content type
application/json
{
  • "email": "user@example.com"
}

Response samples

Content type
application/json
Example
{
  • "status": "available"
}

Check email availability for account activation

Check if email is available for account creation during lead activation.

Security: Public endpoint (no auth required).

  • Requires valid lead_id in 'paid' state to prevent email enumeration
  • Lead ID acts as proof of payment

Use Case: Frontend checks email availability as user types in the signup form after payment, showing inline error with "Sign in instead" link if email is taken.

Difference from /validate-email:

  • /validate-email: Pre-payment UX hint (before checkout)
  • /check-email: Post-payment validation (requires paid lead)
Request Body schema: application/json
required
leadId
required
string [ 1 .. 100 ] characters

Lead ID to validate (must be in paid state)

email
required
string <email> [ 5 .. 254 ] characters

Email to check availability for

Responses

Request samples

Content type
application/json
{
  • "leadId": "lead_abc123def456",
  • "email": "user@example.com"
}

Response samples

Content type
application/json
Example
{
  • "available": true,
  • "email": "user@example.com"
}

Create lead for anonymous checkout

Creates a lead record for the Pay-Before-Signup flow.

Flow:

  1. Frontend calls this with priceId
  2. Backend creates Lead with 24h TTL
  3. Frontend opens Paddle checkout with leadId in customData
  4. Paddle webhook marks lead as paid
  5. Frontend polls /payment/status/{leadId}
  6. When paid, user completes signup via /payment/activate

Security: Public endpoint (no auth required).

Request Body schema: application/json
required
priceId
required
string [ 4 .. 100 ] characters

Paddle price ID (pri_...)

email
string or null <email>

Optional email (captured during checkout)

source
string or null <= 50 characters

Lead source (pricing_page, upgrade_prompt)

Responses

Request samples

Content type
application/json
{
  • "priceId": "pri_01234567890",
  • "email": "user@example.com",
  • "source": "pricing_page"
}

Response samples

Content type
application/json
{
  • "lead_id": "lead_abc123def456",
  • "checkout_url": null
}

Get lead payment status

Poll this endpoint after Paddle checkout to check payment status.

Security: Public endpoint. Lead ID is a UUID, so guessing is impractical.

States:

  • pending: Waiting for payment
  • paid: Payment received, ready for activation
  • completed: Account created
  • expired: TTL exceeded (24h)

Email Conflict Detection (v1.1): When payment_state is paid and lead has email, the response includes activation_blocked and activation_block_reason to warn users early if their email is already registered.

path Parameters
lead_id
required
string

Lead ID from /payment/leads response

Responses

Response samples

Content type
application/json
Example
{
  • "payment_state": "pending",
  • "is_expired": false,
  • "can_activate": false
}

Activate lead into user account (Authoritative Gate)

Creates a user account from a paid lead. This is the authoritative gate for activation - even if pre-validation passed, this endpoint makes the final decision.

Prerequisites:

  • Lead must be in 'paid' state

Response Variants (all return HTTP 200):

  • status: success - Account created, includes auth tokens
  • status: email_taken - Email conflict, includes recovery action
  • status: already_activated - Lead already used (idempotent)

Why not 409 for email conflict?

  • 409 is a dead-end; user doesn't know what to do
  • action: SIGN_IN_TO_CLAIM tells frontend to redirect to login
  • Include paddleCustomerId so support can link subscription if needed

Effects (on success):

  • Creates User account
  • Links Paddle customer/subscription
  • Marks lead as completed
  • Returns auth tokens for immediate login

Security: Public endpoint. Lead validation prevents abuse.

Request Body schema: application/json
required
leadId
required
string [ 1 .. 100 ] characters

Lead ID to activate

email
required
string <email>

Email for the new account

password
required
string [ 8 .. 128 ] characters

Password for the new account

name
string or null <= 100 characters

Display name

Responses

Request samples

Content type
application/json
{
  • "leadId": "lead_abc123def456",
  • "email": "user@example.com",
  • "password": "SecureP@ss123!",
  • "name": "John Doe"
}

Response samples

Content type
application/json
Example
{
  • "status": "success",
  • "user": {
    },
  • "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  • "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
  • "subscription": { }
}

Subscription

Subscription management

Get current subscription

Returns the authenticated user's subscription information.

Security: Requires authentication.

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "has_subscription": true,
  • "plan": "string",
  • "status": "active",
  • "currency": "string",
  • "unit_amount": 0,
  • "current_period_start": "2019-08-24T14:15:22Z",
  • "current_period_end": "2019-08-24T14:15:22Z",
  • "cancel_at_period_end": true,
  • "is_active": true,
  • "is_trial": true,
  • "trial_end": "2019-08-24T14:15:22Z",
  • "features": {
    },
  • "paddle_subscription_id": "string",
  • "paddle_customer_id": "string"
}

Get subscription features

Returns feature flags and limits for the user's current plan.

Security: Requires authentication.

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "goalLaneRequests": 0,
  • "aiToolCalls": 0,
  • "fileSizeLimitMb": 0,
  • "batchProcessing": true,
  • "priorityQueue": true,
  • "turboConversions": true,
  • "concurrentJobs": 0,
  • "batchLimit": 0,
  • "aiQualityTier": "haiku"
}

Create checkout session

Creates a Paddle checkout session for subscription purchase.

Security: Requires authentication. User ID is embedded in checkout customData for webhook association.

Authorizations:
BearerAuth
Request Body schema: application/json
required
priceId
required
string [ 4 .. 100 ] characters

Paddle price ID (pri_...)

successUrl
string or null <uri> <= 500 characters

Redirect URL after successful checkout

cancelUrl
string or null <uri> <= 500 characters

Redirect URL if checkout canceled

Responses

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
{}

Cancel subscription

Cancels the user's subscription at the end of the billing period.

Behavior:

  • Subscription remains active until currentPeriodEnd
  • User retains access until period end
  • No refund for remaining period

Security: Requires authentication.

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "status": "success",
  • "message": "Subscription will be canceled at the end of the billing period",
  • "scheduledCancellation": "2026-02-28T23:59:59Z"
}

Pause subscription

Pauses the subscription at the end of the billing period.

Security: Requires authentication.

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "status": "string",
  • "message": "string",
  • "scheduled_cancellation": "2019-08-24T14:15:22Z"
}

Resume paused subscription

Resumes a paused subscription.

Security: Requires authentication.

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "status": "string",
  • "message": "string",
  • "scheduled_cancellation": "2019-08-24T14:15:22Z"
}

Reactivate subscription scheduled for cancellation

Reactivates a subscription that's scheduled for cancellation.

Use Case: User cancelled their subscription but changed their mind before the current billing period ends.

Behavior:

  • Must be called before the cancellation takes effect
  • Removes scheduled cancellation date
  • Subscription will continue to renew normally

Security: Requires authentication.

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "status": "success",
  • "message": "Subscription has been reactivated and will continue to renew"
}

Switch billing cycle

Switches between monthly and yearly billing.

Behavior:

  • Proration applied immediately
  • New billing cycle starts immediately

Security: Requires authentication.

Authorizations:
BearerAuth
Request Body schema: application/json
required
targetBillingCycle
required
string
Enum: "monthly" "yearly"

Target billing cycle

Responses

Request samples

Content type
application/json
{
  • "targetBillingCycle": "yearly"
}

Response samples

Content type
application/json
{
  • "status": "string",
  • "previousPlan": "string",
  • "newPlan": "string",
  • "message": "string"
}

Get customer portal URL

Returns a Paddle Customer Portal URL for self-service management.

Portal Features:

  • Update payment methods
  • View invoices
  • Cancel subscription

Security: Requires authentication.

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json

Reconcile subscription with Paddle

Forces a sync with Paddle API when webhook hasn't arrived yet.

Use Case: After checkout, user returns but webhook delayed. Pass Paddle IDs from checkout.completed event for direct lookup.

Security: Requires authentication. Rate limited.

Authorizations:
BearerAuth
Request Body schema: application/json
optional
paddleCustomerId
string or null <= 100 characters

Paddle customer ID (ctm_...)

paddleSubscriptionId
string or null <= 100 characters

Paddle subscription ID (sub_...)

Responses

Request samples

Content type
application/json
{
  • "paddleCustomerId": "ctm_abc123",
  • "paddleSubscriptionId": "sub_def456"
}

Response samples

Content type
application/json
{
  • "has_subscription": true,
  • "plan": "string",
  • "status": "active",
  • "currency": "string",
  • "unit_amount": 0,
  • "current_period_start": "2019-08-24T14:15:22Z",
  • "current_period_end": "2019-08-24T14:15:22Z",
  • "cancel_at_period_end": true,
  • "is_active": true,
  • "is_trial": true,
  • "trial_end": "2019-08-24T14:15:22Z",
  • "features": {
    },
  • "paddle_subscription_id": "string",
  • "paddle_customer_id": "string"
}

Invoices

Invoice history

Get invoice history

Returns the user's invoice history from Paddle.

Security: Requires authentication.

Authorizations:
BearerAuth
query Parameters
limit
integer [ 1 .. 100 ]
Default: 20

Maximum number of invoices to return

Responses

Response samples

Content type
application/json
{
  • "invoices": [
    ],
  • "has_more": true
}

Get invoice PDF URL

Returns a URL to download the invoice PDF. URL is valid for a limited time.

Security: Requires authentication. Only returns PDF for invoices belonging to the authenticated user.

Authorizations:
BearerAuth
path Parameters
transaction_id
required
string

Paddle transaction ID (txn_...)

Responses

Response samples

Content type
application/json

Payment Methods

Payment method management

Get current payment method

Returns the user's payment method on file.

Security: Requires authentication. Only returns masked card details (last 4 digits, expiry).

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "hasPaymentMethod": true,
  • "type": "card",
  • "cardBrand": "string",
  • "lastFour": "string",
  • "expiryMonth": 0,
  • "expiryYear": 0
}

Get checkout URL for updating payment method

Returns a Paddle checkout URL to update payment method.

Behavior:

  • Zero-value transaction (no charge)
  • Customer updates payment details
  • If subscription is past_due, shows overdue amount

Security: Requires authentication.

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{}

Usage

Usage tracking

Get current usage for a single metric

Returns current usage statistics for a single metric in the billing period.

Security: Requires authentication.

Query Parameter: metric selects which usage metric to query (default: goal_lane_request).

Authorizations:
BearerAuth
query Parameters
metric
string
Default: "goal_lane_request"

Usage metric to query. Available metrics:

  • product_listing_generation
  • image_social_adaptation
  • video_auto_edit
  • file_conversion
  • goal_lane_request (legacy, default)

Responses

Response samples

Content type
application/json
{
  • "metric": "string",
  • "period_start": "2019-08-24T14:15:22Z",
  • "period_end": "2019-08-24T14:15:22Z",
  • "used": 0,
  • "limit": 0,
  • "remaining": 0,
  • "is_unlimited": true,
  • "percentage_used": 0
}

Get full usage report for authenticated user

Returns a comprehensive usage report across all V1 metrics, including plan limits, feature flags, and platform access.

Security: Requires authentication (Bearer token).

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "plan": "free",
  • "metrics": {
    },
  • "plan_limits": {
    },
  • "features": { },
  • "platforms": { }
}

Get usage report for anonymous user

Returns usage report for an anonymous session (cookie-based). Plan is always "anonymous" with limited quotas. Does not include features or platforms sections.

Security: No Bearer token required. Uses anonymous session cookie.

Responses

Response samples

Content type
application/json
{
  • "plan": "free",
  • "metrics": {
    },
  • "plan_limits": {
    },
  • "features": { },
  • "platforms": { }
}

Get usage history

Returns usage history across all metrics for past billing periods.

Security: Requires authentication.

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
[
  • {
    }
]

Forge

Per-tool-call forge billing. Each successful workflow run debits 1 U via /forge/commit (Worktree B). /forge/quote is the pre-flight check the frontend calls before running a workflow.

Pre-flight quote for a single workflow forge (1 U)

Returns whether the caller has enough capacity to run a workflow forge and, when they don't, a Paddle checkout URL to top up.

Cost. Every successful forge debits exactly 1 U. Free credits (earned via the rebate program) are consumed first; plan quota is consumed only after free credits are exhausted.

Balance. balanceU = remaining_plan_quota + freeCredits for the authenticated user.

Security. Requires authentication.

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "costU": 1,
  • "balanceU": 4,
  • "freeCredits": 1,
  • "sufficient": true,
  • "topUp": {
    }
}

Webhooks

Payment webhook handlers

Handle Paddle webhook events

Receives and processes Paddle webhook events.

Security Requirements:

  1. Signature verification (Paddle-Signature header)
  2. Idempotency check (prevent duplicate processing)
  3. Input validation

Supported Events:

  • transaction.completed - Payment successful
  • transaction.payment_failed - Payment retry failed (dunning)
  • subscription.created - New subscription
  • subscription.updated - Subscription modified
  • subscription.canceled - Subscription cancelled
  • subscription.paused - Subscription paused
  • subscription.resumed - Subscription resumed
  • subscription.trialing - Trial started
  • subscription.activated - Trial ended, billing started
  • subscription.past_due - Payment failed
header Parameters
Paddle-Signature
required
string

Paddle webhook signature for verification

Request Body schema: application/json
required
object

Paddle webhook event payload

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
{
  • "status": "string",
  • "eventType": "string"
}