Skip to content
SoundSyncAudio software platform

API reference

Customer and Serial Management API

Current developer customer tables, serial inventory, imports, purchase history and manual access grants.

Use the current developer-scoped serial routes

For new integrations, use /api/developer/serials rather than the deprecated /api/serials table handlers. Account identity, workspace permissions and assigned products determine access. Supplying another developer ID or broadening a filter does not grant their records.

Lists use page, limit, search, dateRange and supported product filters. Serial inventory distinguishes available and redeemed. Purchases additionally accept channel, productIds, resellerId, provider, refundStatus, recordType, dateFrom and dateTo. IDs in historical or external purchase rows are not automatically refundable managed-checkout order IDs.

Generate and import deliberately

Serial generation requires an owned product and current billing eligibility. Prefix and expiry are inventory settings, not proof of a customer purchase. Reseller serial pools belong in Partners > Licenses; ordinary developer generation rejects reseller assignment.

Download the current CSV template before importing. Upload file as multipart data or send the named customers, serials or purchases JSON array. Product mappings must resolve to allowed products. A 207 result contains row errors and successful rows; correct the failures rather than replaying every row. Purchase imports do not collect payment, transfer billing authority or send purchase marketing messages.

SoundSync

Illustrative owned-product serial generation

Generate inventory, not customer ownership
curl --fail-with-body -sS -X POST "$SOUNDSYNC_API_BASE/api/developer/serials/generate" \
  -H "Authorization: Bearer $DEVELOPER_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"productId":123,"quantity":5,"prefix":"DEMO"}'

Access, history and payment are separate

Activate, reactivate, deactivate, suspend, unsuspend and revoke are administrative serial actions. Hiding an order only changes its supported history presentation. Neither action refunds a payment.

Manual grants give the selected customer access to explicit products. Existing-serial assignment and subscription access grants have single-product restrictions. An access grant does not authorize billing; use the separate supported customer-confirmed payment setup when needed. Keep the reason and grant request reference for reconciliation.

Read access-only history without inventing a payment

GET /api/developer/purchases groups related items under purchase_key. An access-only row uses order_type:access_grant, financial_record:false, invoice_available:false and null order_id/order_item_id. Its access_entitlement_id identifies access, not a paid order. display_order_id falls back to a reference such as ACCESS-123. For a product earned from a subscription, it can instead display the original funding order, also returned as original_subscription_order_id. That display-only link does not make this access row refundable or give it an invoice.

Use recordType:access to select these rows; recordType:order and pool select the other supported record kinds. channel:subscription includes subscription history. These filters do not change ownership or expand product permissions. A subscription payment and a product earned from it are distinct history entries, not two charges. Earned subscription products show zero-cost item totals in the funding order currency; their CSV subtotal and final item total are 0, while the financial order total stays blank. Other non-financial access rows leave financial totals blank. Never add access-only rows to sales revenue or treat their display reference as refund authority.

A manual history entry can retain existing access

A standard product grant can record a manual grant even when the customer already has active access. historyRecorded and alreadyHadAccess describe this result; metadata.manualGrantExistingAccess identifies the retained-access history. It preserves the original entitlement, serial and provenance, without another payment, settlement credit or repeated marketing enrollment.

For that history-only order item, remove-access without deleteRecord:true returns 409 MANUAL_GRANT_HISTORY_ONLY. With deleteRecord:true, it hides the manual history and returns recordOnly:true with zero revocations; the original access remains. Manage the original entitlement or serial separately when an authorized access change is intended. Reuse grantRequestId for the same request after an uncertain response, not for a different grant.

Customer removal is not global account deletion

Removal requires customers.manage and confirm:true or confirmation:"yes". It removes the developer customer relationship and supported associated marketing records. Required purchases, financial history and global account identity are separate. Use the verified privacy-request procedure for broader deletion requests.

Protect the customer login

Workspace-wide contact edits, deletion, customer imports and verification resends require all-products scope and customers.manage. Product-scoped staff can continue using permitted serial and purchase controls; a customer ID cannot expand those permissions.

A developer edit cannot change a linked login email or replace a password. Direct the customer to account settings to verify an email change, or to Reset password for recovery. A contact-only email correction is permitted only when no login is linked and no other developer shares the identity; it clears email verification. Supported name and phone corrections can update a shared customer profile, not just one storefront label.

Read the result of recovery actions

Internal notes are private support records. Activity and plugin-connection reports can contain customer identity and serial diagnostics; never publish them unredacted. Activation reset clears the supported machines and sessions without refunding or granting products.

An access-email action returns an email outcome object plus libraryUrl. HTTP 200 alone does not mean the provider accepted a message: inspect sent, skipped, reason or error. Required sender configuration and the current stored customer identity still apply.

SoundSync

Related customer operations

Refund API

Whole-product/full-order refunds and operation state.

Customer tools

Profile correction, verification and device recovery.

Deprecated serial API

Compatibility-only handlers and their limits.

SoundSync

Requests and responses

GET /api/developer/customers

Search developer-scoped customers

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • query page: integer
  • query limit: integer
  • query search: string
  • query dateRange: string
  • query productId: integer
Returns
HTTP 200: rows, total, page, pageSize.
Notes
Account permissions and assigned products determine scope; query filters cannot expand it. Purchase identifiers can include external/manual references; they are not all eligible managed-payment refund IDs.
Request, response and error reference

Query and header parameters

[
  {
    "name": "page",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  },
  {
    "name": "limit",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  },
  {
    "name": "search",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  },
  {
    "name": "dateRange",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  },
  {
    "name": "productId",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  }
]

No request body fields are read by this operation.

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "rows": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "integer",
              "string"
            ],
            "minLength": 1
          },
          "customer_id": {
            "type": "integer",
            "minimum": 1
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "first_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "email_verification_status": {
            "type": "string"
          }
        },
        "additionalProperties": true
      }
    },
    "total": {
      "type": "integer"
    },
    "page": {
      "type": "integer"
    },
    "pageSize": {
      "type": "integer"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

GET /api/developer/customers/export

Export authorized customers as CSV

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • query page: integer
  • query limit: integer
  • query search: string
  • query dateRange: string
  • query productId: integer
Returns
HTTP 200: text/csv.
Request, response and error reference

Query and header parameters

[
  {
    "name": "page",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  },
  {
    "name": "limit",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  },
  {
    "name": "search",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  },
  {
    "name": "dateRange",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  },
  {
    "name": "productId",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  }
]

No request body fields are read by this operation.

HTTP 200 response (text/csv)

{
  "type": "string",
  "format": "binary"
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

GET /api/developer/customers/demo-csv

Download the customers import template

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Returns
HTTP 200: text/csv.
Request, response and error reference

No request body fields are read by this operation.

HTTP 200 response (text/csv)

{
  "type": "string",
  "format": "binary"
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

POST /api/developer/customers/import

Import reviewed customers rows

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • file: string
  • customers: array
  • productMappings: object. Operation-specific extension data; do not put credentials or unnecessary personal information here.
Returns
HTTP 201: success, importedCount, insertedCount, errors.
Notes
Send a CSV file under file or the named JSON row array. Inspect row errors on 207; do not blindly replay already imported rows. Product mappings do not bypass ownership. Customer imports require customers.manage and all-products scope; purchase imports require entitlements.manage and serial imports require serials.manage, with assigned-product checks. Purchase import does not collect payment or transfer provider billing authority and disables purchase marketing/email activity.
Request, response and error reference

Request (application/json or multipart/form-data)

{
  "type": "object",
  "properties": {
    "file": {
      "type": "string",
      "format": "binary"
    },
    "customers": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "productId": {
            "type": "integer",
            "minimum": 1
          },
          "productName": {
            "type": "string"
          },
          "serialCode": {
            "type": "string"
          },
          "price": {
            "type": "number"
          },
          "salePrice": {
            "type": "number"
          },
          "date": {
            "type": "string"
          }
        },
        "additionalProperties": true
      }
    },
    "productMappings": {
      "type": "object",
      "additionalProperties": true,
      "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
    }
  },
  "additionalProperties": true
}

HTTP 201 response (application/json)

{
  "type": "object",
  "properties": {
    "success": {
      "type": "boolean"
    },
    "importedCount": {
      "type": "integer"
    },
    "insertedCount": {
      "type": "integer"
    },
    "errors": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": true,
        "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
      }
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

GET /api/developer/serials

Search serial inventory and device counts

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • query page: integer
  • query limit: integer
  • query search: string
  • query dateRange: string
  • query productId: integer
  • query status: string
Returns
HTTP 200: rows, total, page, pageSize.
Notes
Account permissions and assigned products determine scope; query filters cannot expand it. Purchase identifiers can include external/manual references; they are not all eligible managed-payment refund IDs.
Request, response and error reference

Query and header parameters

[
  {
    "name": "page",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  },
  {
    "name": "limit",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  },
  {
    "name": "search",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  },
  {
    "name": "dateRange",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  },
  {
    "name": "productId",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  },
  {
    "name": "status",
    "in": "query",
    "schema": {
      "type": "string",
      "enum": [
        "available",
        "redeemed"
      ]
    },
    "required": false,
    "description": ""
  }
]

No request body fields are read by this operation.

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "rows": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "integer",
              "string"
            ],
            "minLength": 1
          },
          "serial": {
            "type": "string"
          },
          "product_id": {
            "type": "integer",
            "minimum": 1
          },
          "product": {
            "type": "string"
          },
          "customer_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "redeemed": {
            "type": "boolean"
          },
          "revoked": {
            "type": "boolean"
          },
          "active_activation_count": {
            "type": "integer"
          },
          "max_activations": {
            "type": "integer"
          },
          "remaining_activations": {
            "type": "integer"
          }
        },
        "additionalProperties": true
      }
    },
    "total": {
      "type": "integer"
    },
    "page": {
      "type": "integer"
    },
    "pageSize": {
      "type": "integer"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

GET /api/developer/serials/export

Export authorized serials as CSV

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • query page: integer
  • query limit: integer
  • query search: string
  • query dateRange: string
  • query productId: integer
Returns
HTTP 200: text/csv.
Request, response and error reference

Query and header parameters

[
  {
    "name": "page",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  },
  {
    "name": "limit",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  },
  {
    "name": "search",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  },
  {
    "name": "dateRange",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  },
  {
    "name": "productId",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  }
]

No request body fields are read by this operation.

HTTP 200 response (text/csv)

{
  "type": "string",
  "format": "binary"
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

GET /api/developer/serials/demo-csv

Download the serials import template

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Returns
HTTP 200: text/csv.
Request, response and error reference

No request body fields are read by this operation.

HTTP 200 response (text/csv)

{
  "type": "string",
  "format": "binary"
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

POST /api/developer/serials/import

Import reviewed serials rows

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • file: string
  • serials: array
  • productMappings: object. Operation-specific extension data; do not put credentials or unnecessary personal information here.
Returns
HTTP 201: success, importedCount, insertedCount, errors.
Notes
Send a CSV file under file or the named JSON row array. Inspect row errors on 207; do not blindly replay already imported rows. Product mappings do not bypass ownership. Customer imports require customers.manage and all-products scope; purchase imports require entitlements.manage and serial imports require serials.manage, with assigned-product checks. Purchase import does not collect payment or transfer provider billing authority and disables purchase marketing/email activity.
Request, response and error reference

Request (application/json or multipart/form-data)

{
  "type": "object",
  "properties": {
    "file": {
      "type": "string",
      "format": "binary"
    },
    "serials": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "productId": {
            "type": "integer",
            "minimum": 1
          },
          "productName": {
            "type": "string"
          },
          "serialCode": {
            "type": "string"
          },
          "price": {
            "type": "number"
          },
          "salePrice": {
            "type": "number"
          },
          "date": {
            "type": "string"
          }
        },
        "additionalProperties": true
      }
    },
    "productMappings": {
      "type": "object",
      "additionalProperties": true,
      "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
    }
  },
  "additionalProperties": true
}

HTTP 201 response (application/json)

{
  "type": "object",
  "properties": {
    "success": {
      "type": "boolean"
    },
    "importedCount": {
      "type": "integer"
    },
    "insertedCount": {
      "type": "integer"
    },
    "errors": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": true,
        "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
      }
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

GET /api/developer/purchases

Search grouped purchase and access history

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • query page: integer
  • query limit: integer
  • query search: string
  • query dateRange: string
  • query productId: integer
  • query channel: value
  • query productIds: string
  • query resellerId: string
  • query provider: value
  • query refundStatus: value
  • query recordType: value
  • query dateFrom: string
  • query dateTo: string
Returns
HTTP 200: rows, total, page, pageSize, canRefund.
Notes
Account permissions and assigned products determine scope; query filters cannot expand it. Purchase rows group items by purchase_key. Access-only rows have order_type:access_grant, financial_record:false, invoice_available:false and null order_id/order_item_id. display_order_id falls back to ACCESS-<entitlement ID>; an earned subscription product can instead display the funding order, with original_subscription_order_id as a separate reference and zero-cost item totals. Neither display value makes the access row a payment, invoice or refundable order. channel:subscription includes subscription history; recordType:access selects access-only rows. canRefund is a caller capability, not per-row eligibility. Purchase identifiers can also include external/manual references; use the refund preview for an eligible managed-payment order.
Request, response and error reference

Query and header parameters

[
  {
    "name": "page",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  },
  {
    "name": "limit",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  },
  {
    "name": "search",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  },
  {
    "name": "dateRange",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  },
  {
    "name": "productId",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  },
  {
    "name": "channel",
    "in": "query",
    "schema": {
      "enum": [
        "all",
        "direct",
        "marketplace",
        "reseller",
        "manual",
        "import",
        "free",
        "subscription",
        "other"
      ]
    },
    "required": false,
    "description": ""
  },
  {
    "name": "productIds",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  },
  {
    "name": "resellerId",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  },
  {
    "name": "provider",
    "in": "query",
    "schema": {
      "enum": [
        "all",
        "stripe",
        "paypal",
        "other",
        "unknown"
      ]
    },
    "required": false,
    "description": ""
  },
  {
    "name": "refundStatus",
    "in": "query",
    "schema": {
      "enum": [
        "all",
        "none",
        "partial",
        "full",
        "unknown"
      ]
    },
    "required": false,
    "description": ""
  },
  {
    "name": "recordType",
    "in": "query",
    "schema": {
      "enum": [
        "all",
        "order",
        "pool",
        "access"
      ]
    },
    "required": false,
    "description": ""
  },
  {
    "name": "dateFrom",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  },
  {
    "name": "dateTo",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  }
]

No request body fields are read by this operation.

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "rows": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "order_id": {
            "type": [
              "integer",
              "string",
              "null"
            ],
            "minLength": 1
          },
          "order_item_id": {
            "type": [
              "integer",
              "string",
              "null"
            ],
            "minLength": 1
          },
          "product_id": {
            "type": "integer",
            "minimum": 1
          },
          "product": {
            "type": "string"
          },
          "customer_id": {
            "type": "integer",
            "minimum": 1
          },
          "status": {
            "type": "string"
          },
          "invoice_available": {
            "type": "boolean"
          },
          "financial_record": {
            "type": "boolean"
          },
          "order_type": {
            "type": "string"
          },
          "purchase_channel": {
            "type": "string"
          },
          "access_entitlement_id": {
            "type": [
              "integer",
              "string"
            ],
            "minLength": 1
          },
          "entitlement_id": {
            "type": [
              "integer",
              "string",
              "null"
            ],
            "minLength": 1
          },
          "entitlement_state": {
            "type": [
              "string",
              "null"
            ]
          },
          "display_order_id": {
            "type": "string"
          },
          "original_subscription_order_id": {
            "type": [
              "integer",
              "string",
              "null"
            ],
            "minLength": 1
          },
          "currency": {
            "type": "string"
          },
          "item_subtotal_cents": {
            "type": "integer"
          },
          "final_total_cents": {
            "type": "integer"
          },
          "total_cents": {
            "type": "integer"
          },
          "purchase_key": {
            "type": "string"
          },
          "occurred_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "order_id": {
                  "type": [
                    "integer",
                    "string",
                    "null"
                  ],
                  "minLength": 1
                },
                "order_item_id": {
                  "type": [
                    "integer",
                    "string",
                    "null"
                  ],
                  "minLength": 1
                },
                "product_id": {
                  "type": "integer",
                  "minimum": 1
                },
                "product": {
                  "type": "string"
                },
                "customer_id": {
                  "type": "integer",
                  "minimum": 1
                },
                "status": {
                  "type": "string"
                },
                "invoice_available": {
                  "type": "boolean"
                },
                "financial_record": {
                  "type": "boolean"
                },
                "order_type": {
                  "type": "string"
                },
                "purchase_channel": {
                  "type": "string"
                },
                "access_entitlement_id": {
                  "type": [
                    "integer",
                    "string"
                  ],
                  "minLength": 1
                },
                "entitlement_id": {
                  "type": [
                    "integer",
                    "string",
                    "null"
                  ],
                  "minLength": 1
                },
                "entitlement_state": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "display_order_id": {
                  "type": "string"
                },
                "original_subscription_order_id": {
                  "type": [
                    "integer",
                    "string",
                    "null"
                  ],
                  "minLength": 1
                },
                "currency": {
                  "type": "string"
                },
                "item_subtotal_cents": {
                  "type": "integer"
                },
                "final_total_cents": {
                  "type": "integer"
                },
                "total_cents": {
                  "type": "integer"
                },
                "purchase_key": {
                  "type": "string"
                },
                "occurred_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "created_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "metadata": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
                }
              },
              "additionalProperties": true
            }
          }
        },
        "additionalProperties": true
      }
    },
    "total": {
      "type": "integer"
    },
    "page": {
      "type": "integer"
    },
    "pageSize": {
      "type": "integer"
    },
    "canRefund": {
      "type": "boolean"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

GET /api/developer/purchases/export

Export authorized purchases as CSV

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • query page: integer
  • query limit: integer
  • query search: string
  • query dateRange: string
  • query productId: integer
Returns
HTTP 200: text/csv.
Request, response and error reference

Query and header parameters

[
  {
    "name": "page",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  },
  {
    "name": "limit",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  },
  {
    "name": "search",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  },
  {
    "name": "dateRange",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  },
  {
    "name": "productId",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  }
]

No request body fields are read by this operation.

HTTP 200 response (text/csv)

{
  "type": "string",
  "format": "binary"
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

GET /api/developer/purchases/demo-csv

Download the purchases import template

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Returns
HTTP 200: text/csv.
Request, response and error reference

No request body fields are read by this operation.

HTTP 200 response (text/csv)

{
  "type": "string",
  "format": "binary"
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

POST /api/developer/purchases/import

Import reviewed purchases rows

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • file: string
  • purchases: array
  • productMappings: object. Operation-specific extension data; do not put credentials or unnecessary personal information here.
Returns
HTTP 201: success, importedCount, insertedCount, errors.
Notes
Send a CSV file under file or the named JSON row array. Inspect row errors on 207; do not blindly replay already imported rows. Product mappings do not bypass ownership. Customer imports require customers.manage and all-products scope; purchase imports require entitlements.manage and serial imports require serials.manage, with assigned-product checks. Purchase import does not collect payment or transfer provider billing authority and disables purchase marketing/email activity.
Request, response and error reference

Request (application/json or multipart/form-data)

{
  "type": "object",
  "properties": {
    "file": {
      "type": "string",
      "format": "binary"
    },
    "purchases": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "productId": {
            "type": "integer",
            "minimum": 1
          },
          "productName": {
            "type": "string"
          },
          "serialCode": {
            "type": "string"
          },
          "price": {
            "type": "number"
          },
          "salePrice": {
            "type": "number"
          },
          "date": {
            "type": "string"
          }
        },
        "additionalProperties": true
      }
    },
    "productMappings": {
      "type": "object",
      "additionalProperties": true,
      "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
    }
  },
  "additionalProperties": true
}

HTTP 201 response (application/json)

{
  "type": "object",
  "properties": {
    "success": {
      "type": "boolean"
    },
    "importedCount": {
      "type": "integer"
    },
    "insertedCount": {
      "type": "integer"
    },
    "errors": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": true,
        "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
      }
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

DELETE /api/developer/customers/{customerId}

Remove supported developer customer information

Auth
Account Bearer JWT
Scope
The developer customer relationship and all-products workspace scope. Mutations require the operation-specific manage permission; a linked login credential is never changed by a contact edit.
Request fields
  • confirm: boolean
  • confirmation: string
  • reason: string
Returns
HTTP 200: ok, alreadyRemoved, already_removed, customer_id, developer_customer_id, message.
Notes
Requires customers.manage, all-products workspace scope and confirm:true or confirmation:"yes". This removes the developer contact relationship and supported marketing records, not the global account, purchases or required financial records. Review the account-closure and privacy-request guides.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "confirm": {
      "type": "boolean"
    },
    "confirmation": {
      "type": "string"
    },
    "reason": {
      "type": "string"
    }
  },
  "additionalProperties": true,
  "anyOf": [
    {
      "properties": {
        "confirm": {
          "const": true
        }
      },
      "required": [
        "confirm"
      ]
    },
    {
      "properties": {
        "confirmation": {
          "type": "string",
          "pattern": "^\\s*[Yy][Ee][Ss]\\s*$"
        }
      },
      "required": [
        "confirmation"
      ]
    }
  ]
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "ok": {
      "type": "boolean"
    },
    "alreadyRemoved": {
      "type": "boolean"
    },
    "already_removed": {
      "type": "boolean"
    },
    "customer_id": {
      "type": "integer",
      "minimum": 1
    },
    "developer_customer_id": {
      "type": [
        "integer",
        "string",
        "null"
      ]
    },
    "message": {
      "type": "string"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

GET /api/developer/serials/available-by-product

Group available serials by product and promotion

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • query page: integer
  • query limit: integer
  • query search: string
  • query dateRange: string
  • query productId: integer
Returns
HTTP 200: products, promos, rows, total, page, pageSize.
Request, response and error reference

Query and header parameters

[
  {
    "name": "page",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  },
  {
    "name": "limit",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  },
  {
    "name": "search",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  },
  {
    "name": "dateRange",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  },
  {
    "name": "productId",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1
    },
    "required": false,
    "description": ""
  }
]

No request body fields are read by this operation.

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "products": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": true,
        "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
      }
    },
    "promos": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": true,
        "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
      }
    },
    "rows": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "integer",
              "string"
            ],
            "minLength": 1
          },
          "serial": {
            "type": "string"
          },
          "product_id": {
            "type": "integer",
            "minimum": 1
          },
          "product": {
            "type": "string"
          },
          "customer_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "redeemed": {
            "type": "boolean"
          },
          "revoked": {
            "type": "boolean"
          },
          "active_activation_count": {
            "type": "integer"
          },
          "max_activations": {
            "type": "integer"
          },
          "remaining_activations": {
            "type": "integer"
          }
        },
        "additionalProperties": true
      }
    },
    "total": {
      "type": "integer"
    },
    "page": {
      "type": "integer"
    },
    "pageSize": {
      "type": "integer"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

GET /api/developer/serials/resellers

List eligible relationship and legacy reseller references

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Returns
HTTP 200: resellers.
Request, response and error reference

No request body fields are read by this operation.

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "resellers": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "integer",
              "string"
            ],
            "minLength": 1
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "relationship_id": {
            "type": "integer",
            "minimum": 1
          },
          "developer_share_bps": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "additionalProperties": true
      }
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

POST /api/developer/serials/generate

Generate unique serials for an owned product

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • productId: integer
  • product_id: integer
  • quantity: integer (required)
  • prefix: string
  • expiresAt: string,null
  • promoPrice: number
  • promoStart: string,null
  • promoExpires: string,null
  • activeFrom: string,null
  • activeTo: string,null
  • batchTag: string
  • notes: string
Returns
HTTP 201: message, count, product, serials.
Notes
product_id and corresponding snake_case date/price aliases remain accepted. Use Partners > Licenses for reseller serial pools; reseller assignment on this route returns PARTNER_POOL_REQUIRED. Existing billing and product-scope checks apply.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "productId": {
      "type": "integer",
      "minimum": 1
    },
    "product_id": {
      "type": "integer",
      "minimum": 1
    },
    "quantity": {
      "type": "integer",
      "minimum": 1,
      "maximum": 1000
    },
    "prefix": {
      "type": "string"
    },
    "expiresAt": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time"
    },
    "promoPrice": {
      "type": "number"
    },
    "promoStart": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time"
    },
    "promoExpires": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time"
    },
    "activeFrom": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time"
    },
    "activeTo": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time"
    },
    "batchTag": {
      "type": "string"
    },
    "notes": {
      "type": "string"
    }
  },
  "required": [
    "quantity"
  ],
  "additionalProperties": true,
  "anyOf": [
    {
      "required": [
        "productId"
      ]
    },
    {
      "required": [
        "product_id"
      ]
    }
  ]
}

HTTP 201 response (application/json)

{
  "type": "object",
  "properties": {
    "message": {
      "type": "string"
    },
    "count": {
      "type": "integer"
    },
    "product": {
      "type": "object",
      "properties": {
        "id": {
          "type": "integer",
          "minimum": 1
        },
        "name": {
          "type": "string"
        }
      },
      "additionalProperties": true
    },
    "serials": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "integer",
              "string"
            ],
            "minLength": 1
          },
          "serial": {
            "type": "string"
          },
          "product_id": {
            "type": "integer",
            "minimum": 1
          },
          "product": {
            "type": "string"
          },
          "customer_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "redeemed": {
            "type": "boolean"
          },
          "revoked": {
            "type": "boolean"
          },
          "active_activation_count": {
            "type": "integer"
          },
          "max_activations": {
            "type": "integer"
          },
          "remaining_activations": {
            "type": "integer"
          }
        },
        "additionalProperties": true
      }
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

POST /api/developer/serials/{serialId}/activate

Apply the activate serial-access action

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • reason: string
  • note: string
Returns
HTTP 200: serial, deactivatedActivations.
Notes
Changes developer-controlled serial access. It is not a provider refund, customer identity transfer or checkout payment. Revocation/suspension/deactivation can affect existing devices.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "reason": {
      "type": "string"
    },
    "note": {
      "type": "string"
    }
  },
  "additionalProperties": true
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "serial": {
      "type": "object",
      "properties": {
        "id": {
          "type": [
            "integer",
            "string"
          ],
          "minLength": 1
        },
        "serial": {
          "type": "string"
        },
        "product_id": {
          "type": "integer",
          "minimum": 1
        },
        "product": {
          "type": "string"
        },
        "customer_id": {
          "type": [
            "integer",
            "null"
          ]
        },
        "status": {
          "type": "string"
        },
        "redeemed": {
          "type": "boolean"
        },
        "revoked": {
          "type": "boolean"
        },
        "active_activation_count": {
          "type": "integer"
        },
        "max_activations": {
          "type": "integer"
        },
        "remaining_activations": {
          "type": "integer"
        }
      },
      "additionalProperties": true
    },
    "deactivatedActivations": {
      "type": "integer"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

POST /api/developer/serials/{serialId}/reactivate

Apply the reactivate serial-access action

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • reason: string
  • note: string
Returns
HTTP 200: serial, deactivatedActivations.
Notes
Changes developer-controlled serial access. It is not a provider refund, customer identity transfer or checkout payment. Revocation/suspension/deactivation can affect existing devices.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "reason": {
      "type": "string"
    },
    "note": {
      "type": "string"
    }
  },
  "additionalProperties": true
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "serial": {
      "type": "object",
      "properties": {
        "id": {
          "type": [
            "integer",
            "string"
          ],
          "minLength": 1
        },
        "serial": {
          "type": "string"
        },
        "product_id": {
          "type": "integer",
          "minimum": 1
        },
        "product": {
          "type": "string"
        },
        "customer_id": {
          "type": [
            "integer",
            "null"
          ]
        },
        "status": {
          "type": "string"
        },
        "redeemed": {
          "type": "boolean"
        },
        "revoked": {
          "type": "boolean"
        },
        "active_activation_count": {
          "type": "integer"
        },
        "max_activations": {
          "type": "integer"
        },
        "remaining_activations": {
          "type": "integer"
        }
      },
      "additionalProperties": true
    },
    "deactivatedActivations": {
      "type": "integer"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

POST /api/developer/serials/{serialId}/deactivate

Apply the deactivate serial-access action

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • reason: string
  • note: string
Returns
HTTP 200: serial, deactivatedActivations.
Notes
Changes developer-controlled serial access. It is not a provider refund, customer identity transfer or checkout payment. Revocation/suspension/deactivation can affect existing devices.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "reason": {
      "type": "string"
    },
    "note": {
      "type": "string"
    }
  },
  "additionalProperties": true
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "serial": {
      "type": "object",
      "properties": {
        "id": {
          "type": [
            "integer",
            "string"
          ],
          "minLength": 1
        },
        "serial": {
          "type": "string"
        },
        "product_id": {
          "type": "integer",
          "minimum": 1
        },
        "product": {
          "type": "string"
        },
        "customer_id": {
          "type": [
            "integer",
            "null"
          ]
        },
        "status": {
          "type": "string"
        },
        "redeemed": {
          "type": "boolean"
        },
        "revoked": {
          "type": "boolean"
        },
        "active_activation_count": {
          "type": "integer"
        },
        "max_activations": {
          "type": "integer"
        },
        "remaining_activations": {
          "type": "integer"
        }
      },
      "additionalProperties": true
    },
    "deactivatedActivations": {
      "type": "integer"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

POST /api/developer/serials/{serialId}/suspend

Apply the suspend serial-access action

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • reason: string
  • note: string
Returns
HTTP 200: serial, deactivatedActivations.
Notes
Changes developer-controlled serial access. It is not a provider refund, customer identity transfer or checkout payment. Revocation/suspension/deactivation can affect existing devices.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "reason": {
      "type": "string"
    },
    "note": {
      "type": "string"
    }
  },
  "additionalProperties": true
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "serial": {
      "type": "object",
      "properties": {
        "id": {
          "type": [
            "integer",
            "string"
          ],
          "minLength": 1
        },
        "serial": {
          "type": "string"
        },
        "product_id": {
          "type": "integer",
          "minimum": 1
        },
        "product": {
          "type": "string"
        },
        "customer_id": {
          "type": [
            "integer",
            "null"
          ]
        },
        "status": {
          "type": "string"
        },
        "redeemed": {
          "type": "boolean"
        },
        "revoked": {
          "type": "boolean"
        },
        "active_activation_count": {
          "type": "integer"
        },
        "max_activations": {
          "type": "integer"
        },
        "remaining_activations": {
          "type": "integer"
        }
      },
      "additionalProperties": true
    },
    "deactivatedActivations": {
      "type": "integer"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

POST /api/developer/serials/{serialId}/unsuspend

Apply the unsuspend serial-access action

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • reason: string
  • note: string
Returns
HTTP 200: serial, deactivatedActivations.
Notes
Changes developer-controlled serial access. It is not a provider refund, customer identity transfer or checkout payment. Revocation/suspension/deactivation can affect existing devices.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "reason": {
      "type": "string"
    },
    "note": {
      "type": "string"
    }
  },
  "additionalProperties": true
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "serial": {
      "type": "object",
      "properties": {
        "id": {
          "type": [
            "integer",
            "string"
          ],
          "minLength": 1
        },
        "serial": {
          "type": "string"
        },
        "product_id": {
          "type": "integer",
          "minimum": 1
        },
        "product": {
          "type": "string"
        },
        "customer_id": {
          "type": [
            "integer",
            "null"
          ]
        },
        "status": {
          "type": "string"
        },
        "redeemed": {
          "type": "boolean"
        },
        "revoked": {
          "type": "boolean"
        },
        "active_activation_count": {
          "type": "integer"
        },
        "max_activations": {
          "type": "integer"
        },
        "remaining_activations": {
          "type": "integer"
        }
      },
      "additionalProperties": true
    },
    "deactivatedActivations": {
      "type": "integer"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

POST /api/developer/serials/{serialId}/revoke

Apply the revoke serial-access action

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • reason: string
  • note: string
Returns
HTTP 200: serial, deactivatedActivations.
Notes
Changes developer-controlled serial access. It is not a provider refund, customer identity transfer or checkout payment. Revocation/suspension/deactivation can affect existing devices.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "reason": {
      "type": "string"
    },
    "note": {
      "type": "string"
    }
  },
  "additionalProperties": true
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "serial": {
      "type": "object",
      "properties": {
        "id": {
          "type": [
            "integer",
            "string"
          ],
          "minLength": 1
        },
        "serial": {
          "type": "string"
        },
        "product_id": {
          "type": "integer",
          "minimum": 1
        },
        "product": {
          "type": "string"
        },
        "customer_id": {
          "type": [
            "integer",
            "null"
          ]
        },
        "status": {
          "type": "string"
        },
        "redeemed": {
          "type": "boolean"
        },
        "revoked": {
          "type": "boolean"
        },
        "active_activation_count": {
          "type": "integer"
        },
        "max_activations": {
          "type": "integer"
        },
        "remaining_activations": {
          "type": "integer"
        }
      },
      "additionalProperties": true
    },
    "deactivatedActivations": {
      "type": "integer"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

POST /api/developer/serials/{serialId}/customer-portal-order/hide

Hide the supported order from customer history

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • reason: string
  • note: string
Returns
HTTP 200: serialId, serial, orderId, customerPortalHidden.
Notes
Changes history presentation, not product ownership or payment/refund state.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "reason": {
      "type": "string"
    },
    "note": {
      "type": "string"
    }
  },
  "additionalProperties": true
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "serialId": {
      "type": [
        "integer",
        "string"
      ],
      "minLength": 1
    },
    "serial": {
      "type": "string"
    },
    "orderId": {
      "type": [
        "integer",
        "string"
      ],
      "minLength": 1
    },
    "customerPortalHidden": {
      "type": "boolean"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

POST /api/developer/serials/{serialId}/customer-portal-order/show

Show the supported order in customer history

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • reason: string
  • note: string
Returns
HTTP 200: serialId, serial, orderId, customerPortalHidden.
Notes
Changes history presentation, not product ownership or payment/refund state.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "reason": {
      "type": "string"
    },
    "note": {
      "type": "string"
    }
  },
  "additionalProperties": true
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "serialId": {
      "type": [
        "integer",
        "string"
      ],
      "minLength": 1
    },
    "serial": {
      "type": "string"
    },
    "orderId": {
      "type": [
        "integer",
        "string"
      ],
      "minLength": 1
    },
    "customerPortalHidden": {
      "type": "boolean"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

GET /api/developer/purchases/filter-options

Read permitted purchase product and reseller filters

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Returns
HTTP 200: products, resellers.
Request, response and error reference

No request body fields are read by this operation.

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "products": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "minimum": 1
          },
          "name": {
            "type": "string"
          }
        },
        "additionalProperties": true
      }
    },
    "resellers": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": true,
        "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
      }
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

POST /api/developer/purchases/manual-grant

Grant authorized product access without collecting payment

Auth
Account Bearer JWT
Scope
Authenticated account, effective developer workspace, applicable permissions and product ownership. Read the operation notes for legacy or owner-only restrictions.
Request fields
  • productId: integer
  • productIds: array
  • email: string
  • firstName: string
  • lastName: string
  • customerId: integer
  • grantType: string
  • grantRequestId: string
  • reason: string
  • note: string
  • serialId: integer
  • sendProductPurchaseEmails: boolean
  • subscriptionGrantDuration: week | month | three_months | custom | lifetime
  • subscriptionGrantEndDate: string
Returns
HTTP 201: success, grantType, count, createdCount, skippedCount, created, grants, products, purchases, items, entitlements, serials, historyRecorded, alreadyHadAccess.
Notes
Choose explicit products and customer identity. Existing-serial assignment and subscription grants are single-product operations. Reuse grantRequestId only for the same intended grant. A standard product grant can record manual history even when active access already exists; historyRecorded/alreadyHadAccess identify that result. Existing entitlement provenance and serials are retained; this is not a second payment, repeated marketing enrollment or a settlement credit. Subscription grants require subscriptionGrantDuration; custom also requires subscriptionGrantEndDate as YYYY-MM-DD. A timed manual keep-products grant immediately owns the included product snapshot without payment; lifetime grants remain complimentary ongoing access. Future billing requires separate payment setup, preview and customer confirmation. Explicit developer access removal can revoke the manual grant; cancellation or expiry of its subscription does not remove the timed keep-products gift.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "productId": {
      "type": "integer",
      "minimum": 1
    },
    "productIds": {
      "type": "array",
      "items": {
        "type": "integer",
        "minimum": 1
      }
    },
    "email": {
      "type": "string",
      "format": "email"
    },
    "firstName": {
      "type": "string"
    },
    "lastName": {
      "type": "string"
    },
    "customerId": {
      "type": "integer",
      "minimum": 1
    },
    "grantType": {
      "type": "string"
    },
    "grantRequestId": {
      "type": "string"
    },
    "reason": {
      "type": "string"
    },
    "note": {
      "type": "string"
    },
    "serialId": {
      "type": "integer",
      "minimum": 1
    },
    "sendProductPurchaseEmails": {
      "type": "boolean"
    },
    "subscriptionGrantDuration": {
      "type": "string",
      "enum": [
        "week",
        "month",
        "three_months",
        "custom",
        "lifetime"
      ]
    },
    "subscriptionGrantEndDate": {
      "type": "string",
      "format": "date"
    }
  },
  "additionalProperties": true
}

HTTP 201 response (application/json)

{
  "type": "object",
  "properties": {
    "success": {
      "type": "boolean"
    },
    "grantType": {
      "type": "string"
    },
    "count": {
      "type": "integer"
    },
    "createdCount": {
      "type": "integer"
    },
    "skippedCount": {
      "type": "integer"
    },
    "created": {
      "type": "boolean"
    },
    "grants": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": true,
        "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
      }
    },
    "products": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "minimum": 1
          },
          "name": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "shortDescription": {
            "type": [
              "string",
              "null"
            ]
          },
          "price": {
            "type": "number"
          },
          "salePrice": {
            "type": [
              "number",
              "null"
            ]
          },
          "regularPrice": {
            "type": "number"
          },
          "displayPrice": {
            "type": "number"
          },
          "priceDisplay": {
            "type": "string"
          },
          "regularPriceDisplay": {
            "type": "string"
          },
          "salePriceDisplay": {
            "type": [
              "string",
              "null"
            ]
          },
          "isOnSale": {
            "type": "boolean"
          },
          "currency": {
            "type": "string"
          },
          "imageUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "coverImage": {
            "type": [
              "string",
              "null"
            ]
          },
          "developerId": {
            "type": "integer",
            "minimum": 1
          },
          "developerName": {
            "type": "string"
          },
          "developer": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "integer",
                "minimum": 1
              },
              "name": {
                "type": "string"
              },
              "slug": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "profileUrl": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "verified": {
                "type": "boolean"
              }
            },
            "additionalProperties": true
          },
          "productType": {
            "type": "string"
          },
          "productTypes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "categories": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "url": {
            "type": "string"
          },
          "subscriptionEnabled": {
            "type": "boolean"
          },
          "billingInterval": {
            "type": [
              "string",
              "null"
            ]
          },
          "checkoutEligible": {
            "type": "boolean"
          },
          "capabilities": {
            "type": "object",
            "additionalProperties": true,
            "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
          },
          "requiresActivation": {
            "type": "boolean"
          },
          "supportsSerials": {
            "type": "boolean"
          },
          "supportsDownloads": {
            "type": "boolean"
          },
          "marketplaceAsset": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
          }
        },
        "additionalProperties": true
      }
    },
    "purchases": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": true,
        "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
      }
    },
    "items": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": true,
        "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
      }
    },
    "entitlements": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": true,
        "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
      }
    },
    "serials": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "integer",
              "string"
            ],
            "minLength": 1
          },
          "serial": {
            "type": "string"
          },
          "product_id": {
            "type": "integer",
            "minimum": 1
          },
          "product": {
            "type": "string"
          },
          "customer_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "redeemed": {
            "type": "boolean"
          },
          "revoked": {
            "type": "boolean"
          },
          "active_activation_count": {
            "type": "integer"
          },
          "max_activations": {
            "type": "integer"
          },
          "remaining_activations": {
            "type": "integer"
          }
        },
        "additionalProperties": true
      }
    },
    "historyRecorded": {
      "type": "boolean"
    },
    "alreadyHadAccess": {
      "type": "boolean"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

GET /api/developer/customers/{customerId}/activity

Read customer activity

Auth
Account Bearer JWT
Scope
customers.view and all-products workspace scope; queries stay developer-scoped.
Request fields
  • query limit: integer
Returns
HTTP 200: events.
Notes
Activity uses camelCase fields; connection and note rows use database-style names. Plugin connections additionally filter fields: customers.view permits customer identity, device_hash and engagement_signal_id; serials.view permits serial_id/serial_code. Opaque failure_reason and metadata require both, otherwise null/{}. Client parameters cannot grant access. Keep permitted diagnostics private. Internal notes are not customer-visible messages. Notes return the latest 100 rows.
Request, response and error reference

Query and header parameters

[
  {
    "name": "limit",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "default": 25
    },
    "required": false,
    "description": ""
  }
]

No request body fields are read by this operation.

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "events": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": 1
              },
              {
                "type": "string",
                "pattern": "^[1-9][0-9]*$"
              }
            ],
            "description": "A positive identifier. PostgreSQL bigint fields can be decimal strings; preserve them without lossy numeric conversion."
          },
          "developerId": {
            "type": "integer",
            "minimum": 1
          },
          "actorType": {
            "type": [
              "string",
              "null"
            ]
          },
          "actorUserId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "customerId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "customerEmail": {
            "type": [
              "string",
              "null"
            ]
          },
          "customerName": {
            "type": [
              "string",
              "null"
            ]
          },
          "productId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "productName": {
            "type": [
              "string",
              "null"
            ]
          },
          "serialId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "serialCode": {
            "type": [
              "string",
              "null"
            ]
          },
          "entitlementId": {
            "anyOf": [
              {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": 1
                  },
                  {
                    "type": "string",
                    "pattern": "^[1-9][0-9]*$"
                  }
                ],
                "description": "A positive identifier. PostgreSQL bigint fields can be decimal strings; preserve them without lossy numeric conversion."
              },
              {
                "type": "null"
              }
            ]
          },
          "orderId": {
            "anyOf": [
              {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": 1
                  },
                  {
                    "type": "string",
                    "pattern": "^[1-9][0-9]*$"
                  }
                ],
                "description": "A positive identifier. PostgreSQL bigint fields can be decimal strings; preserve them without lossy numeric conversion."
              },
              {
                "type": "null"
              }
            ]
          },
          "eventType": {
            "type": "string"
          },
          "severity": {
            "type": "string"
          },
          "message": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
          }
        },
        "additionalProperties": true
      }
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

GET /api/developer/customers/{customerId}/plugin-connections

Read customer plugin-connection activity

Auth
Account Bearer JWT
Scope
customers.view and all-products workspace scope; queries stay developer-scoped.
Request fields
  • query limit: integer
Returns
HTTP 200: connections.
Notes
Activity uses camelCase fields; connection and note rows use database-style names. Plugin connections additionally filter fields: customers.view permits customer identity, device_hash and engagement_signal_id; serials.view permits serial_id/serial_code. Opaque failure_reason and metadata require both, otherwise null/{}. Client parameters cannot grant access. Keep permitted diagnostics private. Internal notes are not customer-visible messages. Notes return the latest 100 rows.
Request, response and error reference

Query and header parameters

[
  {
    "name": "limit",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "default": 25
    },
    "required": false,
    "description": ""
  }
]

No request body fields are read by this operation.

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "connections": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": 1
              },
              {
                "type": "string",
                "pattern": "^[1-9][0-9]*$"
              }
            ],
            "description": "A positive identifier. PostgreSQL bigint fields can be decimal strings; preserve them without lossy numeric conversion."
          },
          "developer_id": {
            "type": "integer",
            "minimum": 1
          },
          "customer_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "product_id": {
            "type": "integer",
            "minimum": 1
          },
          "serial_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "developer_customer_id": {
            "anyOf": [
              {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": 1
                  },
                  {
                    "type": "string",
                    "pattern": "^[1-9][0-9]*$"
                  }
                ],
                "description": "A positive identifier. PostgreSQL bigint fields can be decimal strings; preserve them without lossy numeric conversion."
              },
              {
                "type": "null"
              }
            ]
          },
          "key_id": {
            "anyOf": [
              {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": 1
                  },
                  {
                    "type": "string",
                    "pattern": "^[1-9][0-9]*$"
                  }
                ],
                "description": "A positive identifier. PostgreSQL bigint fields can be decimal strings; preserve them without lossy numeric conversion."
              },
              {
                "type": "null"
              }
            ]
          },
          "engagement_signal_id": {
            "anyOf": [
              {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": 1
                  },
                  {
                    "type": "string",
                    "pattern": "^[1-9][0-9]*$"
                  }
                ],
                "description": "A positive identifier. PostgreSQL bigint fields can be decimal strings; preserve them without lossy numeric conversion."
              },
              {
                "type": "null"
              }
            ]
          },
          "event_kind": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "plugin_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "environment": {
            "type": [
              "string",
              "null"
            ]
          },
          "device_hash": {
            "type": [
              "string",
              "null"
            ]
          },
          "failure_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "product_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer_email": {
            "type": [
              "string",
              "null"
            ]
          },
          "serial_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
          }
        },
        "additionalProperties": true
      }
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

GET /api/developer/customers/{customerId}/notes

Read internal support notes

Auth
Account Bearer JWT
Scope
customers.view and all-products workspace scope; queries stay developer-scoped.
Returns
HTTP 200: notes.
Notes
Activity uses camelCase fields; connection and note rows use database-style names. Plugin connections additionally filter fields: customers.view permits customer identity, device_hash and engagement_signal_id; serials.view permits serial_id/serial_code. Opaque failure_reason and metadata require both, otherwise null/{}. Client parameters cannot grant access. Keep permitted diagnostics private. Internal notes are not customer-visible messages. Notes return the latest 100 rows.
Request, response and error reference

No request body fields are read by this operation.

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "notes": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": 1
              },
              {
                "type": "string",
                "pattern": "^[1-9][0-9]*$"
              }
            ],
            "description": "A positive identifier. PostgreSQL bigint fields can be decimal strings; preserve them without lossy numeric conversion."
          },
          "developer_id": {
            "type": "integer",
            "minimum": 1
          },
          "customer_id": {
            "type": "integer",
            "minimum": 1
          },
          "author_user_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "note": {
            "type": "string"
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "additionalProperties": true
      }
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

POST /api/developer/customers/{customerId}/notes

Add an internal customer support note

Auth
Account Bearer JWT
Scope
customers.manage and all-products workspace scope.
Request fields
  • note: string (required)
Returns
HTTP 201: note.
Notes
Does not send the note to the customer. Avoid credentials, full serials and unnecessary sensitive information.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "note": {
      "type": "string"
    }
  },
  "required": [
    "note"
  ],
  "additionalProperties": true
}

HTTP 201 response (application/json)

{
  "type": "object",
  "properties": {
    "note": {
      "type": "object",
      "properties": {
        "id": {
          "anyOf": [
            {
              "type": "integer",
              "minimum": 1
            },
            {
              "type": "string",
              "pattern": "^[1-9][0-9]*$"
            }
          ],
          "description": "A positive identifier. PostgreSQL bigint fields can be decimal strings; preserve them without lossy numeric conversion."
        },
        "developer_id": {
          "type": "integer",
          "minimum": 1
        },
        "customer_id": {
          "type": "integer",
          "minimum": 1
        },
        "author_user_id": {
          "type": [
            "integer",
            "null"
          ]
        },
        "note": {
          "type": "string"
        },
        "created_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "updated_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        }
      },
      "additionalProperties": true
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

POST /api/developer/customers/{customerId}/reset-activations

Reset eligible customer device activations

Auth
Account Bearer JWT
Scope
entitlements.manage and all-products workspace scope.
Request fields
  • productId: integer
Returns
HTTP 200: reset, serialCount, resetCount.
Notes
Read the returned reset counts. A reset is not a refund, a new product grant or permission for recurring billing.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "productId": {
      "type": "integer",
      "minimum": 1
    }
  },
  "additionalProperties": true
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "reset": {
      "type": "boolean"
    },
    "serialCount": {
      "type": "integer"
    },
    "resetCount": {
      "type": "integer"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

POST /api/developer/customers/{customerId}/resend-library-email

Send the supported customer access email

Auth
Account Bearer JWT
Scope
customers.manage and all-products workspace scope.
Request fields
  • productId: integer
Returns
HTTP 200: email, libraryUrl.
Notes
Uses the stored customer identity, not an arbitrary destination email. This is a transactional access message, not marketing enrollment or proof of inbox delivery.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "productId": {
      "type": "integer",
      "minimum": 1
    }
  },
  "additionalProperties": true
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "email": {
      "type": "object",
      "properties": {
        "sent": {
          "type": "boolean"
        },
        "skipped": {
          "type": "boolean"
        },
        "reason": {
          "type": "string"
        },
        "error": {
          "type": "string"
        },
        "providerMessageId": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "additionalProperties": true
    },
    "libraryUrl": {
      "type": "string"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

POST /api/developer/customers/{customerId}/resend-activation-email

Send the supported customer access email

Auth
Account Bearer JWT
Scope
customers.manage and all-products workspace scope.
Request fields
  • productId: integer
Returns
HTTP 200: email, libraryUrl.
Notes
Uses the stored customer identity, not an arbitrary destination email. This is a transactional access message, not marketing enrollment or proof of inbox delivery.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "productId": {
      "type": "integer",
      "minimum": 1
    }
  },
  "additionalProperties": true
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "email": {
      "type": "object",
      "properties": {
        "sent": {
          "type": "boolean"
        },
        "skipped": {
          "type": "boolean"
        },
        "reason": {
          "type": "string"
        },
        "error": {
          "type": "string"
        },
        "providerMessageId": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "additionalProperties": true
    },
    "libraryUrl": {
      "type": "string"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 401, 403, 404, 409, 500, 503.

Connection diagnostics respect record permissions

Permission to inspect a product integration does not grant access to its customer identities or serial keys. Product connection diagnostics require credentials.view and an assigned product. Customer identity and device-linked fields additionally require customers.view; serial fields require serials.view. Restricted fields are returned as null.

Failure explanations and arbitrary nested metadata can repeat customer or serial details, so they are returned only when both record permissions are present; otherwise the explanation is null and metadata is empty. Customer-specific connection history also requires customers.view and all-products scope. Event kind, status, version, environment and timestamps remain available to permitted diagnostic users.

Permanent gifts and paid subscription ownership

A timed manual keep-products grant gives the customer permanent ownership of the products included when the developer makes that grant, without a payment. That original product snapshot remains owned after subscription cancellation, expiry or incomplete billing setup. Explicit developer access removal can revoke the grant. New products added later are not retroactively included.

A lifetime manual grant instead provides complimentary ongoing subscription access; it is not the timed-grant ownership model or evidence of a paid invoice. Ordinary access-only grants and unpaid checkout trials also do not create paid ownership.

Paid keep-products invoices establish their own product snapshots. Refunding or reversing a paid purchase affects that purchase, not an independent manual ownership grant. Future paid subscription benefits still require the separate billing setup, preview and customer confirmation; a gift is not permission to charge a card.