Paddle Billing ExecModule
Overview
PaddleModule connects ValkyrAI workflows to the current Paddle Billing API through the native map I/O ExecModule ABI. Workflow Studio discovers it as PaddleModule. A server-side Paddle API key resolves only from an encrypted IntegrationAccount; workflow inputs cannot supply credentials or arbitrary endpoints.
The initial operation set covers the core SaaS revenue graph:
list_products,get_product,create_product,update_productlist_customers,get_customer,create_customer,update_customerlist_subscriptions,get_subscriptionlist_transactions,get_transaction
All list operations use Paddle cursor pagination and bounded outputs. Only GET requests retry automatically. Product and customer writes run once so an ambiguous timeout cannot create or mutate a resource twice.
Usage
- In Paddle, create a server-side API key under Developer tools → Authentication.
- Grant only the product, customer, subscription, and transaction permissions required by the workflow.
- Store the API key in the encrypted
apiKeyfield of anIntegrationAccountand set its status toREADY. - Bind the account through
ExecModuleConfig.authConfig.integrationAccount. - Select
productionorsandbox; the module maps this choice to a fixed Paddle host. - Read the target resource before an update and reconcile Paddle state before repeating any write after an ambiguous failure.
Paddle client-side tokens are not accepted. They are intended for Paddle.js and do not authorize the server-side Billing API operations exposed here.
Inputs
| Name | Type | Requirement | Default | Description and constraints |
|---|---|---|---|---|
operation | string | Required | None | One of the 12 documented operations. |
resourceId | string | Get/update operations | None | Typed Paddle ID: pro_…, ctm_…, sub_…, or txn_…, according to the operation. |
resource | object or JSON string | Product/customer create or update | None | Allowlisted payload, maximum 256 KiB. |
environment | string | Optional | production | production or sandbox; maps only to an official fixed API host. |
statusFilter | string | Optional list filter | None | Comma-separated allowlisted statuses for the selected resource. |
customerId | string | Optional subscription/transaction filter | None | Typed ctm_… Paddle customer ID. |
subscriptionId | string | Optional transaction filter | None | Typed sub_… Paddle subscription ID. |
afterCursor | string | Optional list continuation | None | Opaque typed Paddle ID from nextCursor. |
limit | integer | Optional | 50 | Maximum emitted resources, 1–10,000. |
returnAll | boolean | Optional | false | Follow validated provider continuation pages until exhaustion or the result cap. |
Product payloads accept only name, tax_category, description, image_url, custom_data, and status. Product creation requires name. image_url must be a public HTTPS URL.
Customer payloads accept only email, name, locale, custom_data, and status. Customer creation requires a valid bounded email. custom_data, when present, must be an object or null.
Outputs
| Name | Type | When present | Description |
|---|---|---|---|
status | string | Always | success or error. |
operation | string | Always | Normalized operation name. |
attempts | integer | Always | Provider attempts across all pages. |
httpStatus | integer | Provider responded | Last Paddle HTTP status. |
data | object | Single-resource/write success | Paddle resource from the response data object. |
id | string | Resource response includes it | Safe Paddle resource identity. |
resourceStatus | string | Resource response includes status | Paddle resource lifecycle status without replacing the module status. |
items / count | array / integer | List success | Bounded resources and emitted count. |
hasMore | boolean | List success | Whether Paddle reported another page. |
nextCursor | string | Another page exists | Validated typed after cursor from Paddle's meta.pagination.next URL. |
requestId | string | Paddle returns one | Safe request-trace reference. |
error | object | Failure | Safe {code, message, httpStatus?, retryable} details. |
API keys and authorization headers never enter outputs. Provider error details are bounded and passed through ValkyrAI's sensitive-data redaction policy.
IntegrationAccount Requirements
| Setting | Requirement |
|---|---|
| Provider | Paddle Billing server-side API |
| Permissions | Least-privilege read/write for products and customers; read for subscriptions and transactions |
apiKey | Paddle server-side API key in an encrypted SecureField |
password | Legacy encrypted key fallback only; prefer apiKey |
status | Must be READY |
New-format Paddle keys identify their environment (live or sdbx). The workflow environment must match the key's intended environment. Key creation, expiry, rotation, revocation, and permission changes belong to the platform integration-account lifecycle.
Configuration
{
"version": "1.0.0",
"authConfig": {
"authStrategy": 1,
"integrationAccount": "integration-account:paddle-revenue-ops"
},
"retryPolicy": {
"maxAttempts": 3,
"backoffStrategy": "EXPONENTIAL",
"initialDelayMs": 1000,
"maxDelayMs": 60000,
"jitter": false
},
"executionConfig": {"timeoutMs": 30000},
"payloadConfig": {
"parameters": "{\"operation\":\"list_subscriptions\",\"environment\":\"production\",\"statusFilter\":\"active\",\"limit\":50}"
}
}
The integration-account value is symbolic. Persisted workflows use the generated relationship rather than a plaintext key.
Operations
| Operation | Paddle behavior | Side effect |
|---|---|---|
list_products | Lists bounded catalog products with optional status/cursor filters. | Read-only; safe retries. |
get_product | Reads one typed product ID. | Read-only; safe retries. |
create_product | Creates one allowlisted product; name is required. | Catalog creation; single attempt. |
update_product | Applies an allowlisted partial product update. | Catalog mutation; single attempt. |
list_customers | Lists bounded customers with optional status/cursor filters. | Read-only; safe retries. |
get_customer | Reads one typed customer ID. | Read-only; safe retries. |
create_customer | Creates one customer with a valid email. | Customer creation; single attempt. |
update_customer | Applies an allowlisted partial customer update. | Customer mutation; single attempt. |
list_subscriptions | Lists subscriptions, optionally filtered by customer and status. | Read-only; safe retries. |
get_subscription | Reads one typed subscription ID. | Read-only; safe retries. |
list_transactions | Lists transactions, optionally filtered by customer, subscription, and status. | Read-only; safe retries. |
get_transaction | Reads one typed transaction ID. | Read-only; safe retries. |
Errors and Failure Modes
| Code | Typical cause | Retryable | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Missing/invalid ID, environment, filter, email, URL, or payload field. | No | Correct input; no unsafe request was sent. |
UNSUPPORTED_OPERATION | Unknown operation. | No | Select a documented operation. |
INTEGRATION_ACCOUNT_REQUIRED | No bound account. | No | Bind a Paddle IntegrationAccount. |
INTEGRATION_ACCOUNT_NOT_READY | Account status is not READY. | No | Repair or reconnect the account. |
PADDLE_HTTP_400 | Paddle rejected a field or business rule. | No | Inspect the safe error detail and correct the request. |
PADDLE_HTTP_401 / 403 | Key invalid/expired or permission missing. | No | Rotate or reauthorize the IntegrationAccount with least privilege. |
PADDLE_HTTP_404 | Resource is absent or invisible to the key. | No | Re-read/list with the same environment and account. |
PADDLE_HTTP_409 | Provider state conflicts with the requested update. | No | Read current state and rebuild the mutation. |
PADDLE_HTTP_429 / 5xx | Rate limit or transient provider failure. | Yes for reads | Honor Retry-After; reconcile writes before repeating them. |
NETWORK_ERROR | Timeout, DNS, TLS, or connectivity failure. | Yes for reads | Verify connectivity and Paddle status. |
RESPONSE_TOO_LARGE | Response exceeded 5 MiB. | No | Lower the limit or split the workflow. |
INVALID_PROVIDER_RESPONSE | Expected data/pagination shape was absent or the continuation URL was untrusted. | No | Use requestId to verify API compatibility; never follow the rejected URL. |
Example
Create a catalog product for an approved automation package:
{
"operation": "create_product",
"resource": {
"name": "ValkyrAI Governed Operations",
"description": "Production workflow automation package",
"tax_category": "standard",
"custom_data": {
"workflowFamily": "governed-operations",
"source": "valkyrai"
}
}
}
Expected result:
{
"status": "success",
"operation": "create_product",
"attempts": 1,
"httpStatus": 201,
"id": "pro_01h00000000000000000000000",
"data": {
"id": "pro_01h00000000000000000000000",
"name": "ValkyrAI Governed Operations",
"status": "active"
}
}
Notes
- Pagination: Paddle list endpoints use cursor-based pagination. The module requests at most 200 resources per page (30 for transactions), checks
meta.pagination.has_more, validates thenextURL against the selected fixed Paddle host, and emits only its typedaftercursor. It never follows a caller-supplied URL. Total output is capped at 10,000 resources. - Rate limits: reads retry HTTP 408, 429, 500, 502, 503, and 504 according to
RetryPolicy, honoring numericRetry-After. Writes never retry automatically. - API limits: provider responses are capped at 5 MiB and structured write payloads at 256 KiB. Text, email, URL, status, cursor, and ID fields have explicit local bounds.
- Idempotency: product/customer creates and updates are single-attempt. Use caller-owned
custom_datacorrelation fields where appropriate, and reconcile Paddle state after an ambiguous timeout before resubmitting. - Destructive behavior: this release exposes no archive/delete, subscription pause/cancel/resume, transaction creation/revision, adjustment/refund, payment-method deletion, or notification-setting mutation operations.
- Security: only official production/sandbox Paddle hosts are selectable. API keys remain in IntegrationAccount SecureFields; typed IDs, allowlisted fields, trusted continuation validation, response bounds, and redacted provider faults prevent arbitrary routing and secret reflection.
- External verification: request construction, cursor handling, retry safety, validation, response mapping, redaction, and metadata discovery have deterministic local tests. Live Paddle permissions, catalog rules, tax behavior, customer deduplication, rate limits, and billing effects require separately authorized provider credentials and are not exercised in repository tests.
- Deferred operations: prices, discounts, addresses, businesses, subscription mutations, transaction creation/revision, adjustments/refunds, reports, metrics, events, notifications, customer portals, simulations, webhook triggers, key rotation, and arbitrary API calls.
- Functional references: n8n Paddle node, Paddle API reference, Paddle authentication, and Paddle pagination.