Guide
Licensing API
Endpoint reference for claim, claim-status, activate, customer-login activate, validate, and deactivate.
SoundSync
Documented licensing endpoints
Use v1 endpoints for new custom integrations.
/api/license/v1/claim
Start a serial claim when customer identity or email verification is required.
- Auth
- x-ss-app-id + x-ss-plugin-key + x-ss-plugin-secret OR x-ss-app-id + x-ss-product-id + x-soundsync-public-client (matching generated native client)
- Scope
- Product, app, credential, environment, and machine scoped.
- Request fields
- instance_id: string
- machine_id: string
- product_id: integer
- plugin_version: string
- version: string
- license_key: string (required)
- email: string (required)
- Returns
- pending_email_verification with claim_id/claimId and emailSent when verification is required.
- Side effects
- Creates or restores a pending serial claim and can send the verification email.
- Notes
- Requires a serial/license_key, customer email and instance_id (machine_id is an alias). A pending response includes claim_id and emailSent:true; it does not activate the device. 503 VERIFICATION_EMAIL_FAILED with emailSent:false is not confirmed delivery or authorization: retry the claim deliberately after recovery. Owner mismatch/login-required errors do not transfer ownership. Keep the same serial and machine through claim-status and final activation. A compatibility CUSTOMER_ACCOUNT_EXISTS response directs account login/password recovery, not offline access.
Request, response and error reference
Request (application/json)
{
"type": "object",
"properties": {
"instance_id": {
"type": "string"
},
"machine_id": {
"type": "string"
},
"product_id": {
"type": "integer",
"minimum": 1
},
"plugin_version": {
"type": "string"
},
"version": {
"type": "string"
},
"license_key": {
"type": "string"
},
"email": {
"type": "string",
"format": "email"
}
},
"required": [
"license_key",
"email"
],
"additionalProperties": true,
"anyOf": [
{
"required": [
"instance_id"
]
},
{
"required": [
"machine_id"
]
}
]
}
HTTP 200 response (application/json)
{
"type": "object",
"properties": {
"ok": {
"type": "boolean"
},
"status": {
"type": "string"
},
"code": {
"type": "string"
},
"claim_id": {
"type": "string"
},
"claimId": {
"type": "string"
},
"emailSent": {
"type": "boolean"
},
"message": {
"type": "string"
},
"nextAction": {
"type": "string"
},
"passwordResetUrl": {
"type": "string"
},
"claimRequired": {
"type": "boolean"
},
"ownerVerificationRequired": {
"type": "boolean"
},
"requiredFields": {
"type": "array",
"items": {
"type": "string"
}
},
"claim_activation_token": {
"type": "string"
},
"claimActivationToken": {
"type": "string"
}
},
"additionalProperties": true
}
Documented failures: 400, 401, 403, 429, 500, 503.
Operation errors: IDENTITY_FIELDS_REQUIRED, INVALID_EMAIL, SERIAL_OWNER_EMAIL_MISMATCH, SERIAL_OWNER_LOGIN_REQUIRED, VERIFICATION_EMAIL_FAILED.
/api/license/v1/claim-status
Restore a pending serial claim and receive claim activation material after email verification.
- Auth
- x-ss-app-id + x-ss-plugin-key + x-ss-plugin-secret OR x-ss-app-id + x-ss-product-id + x-soundsync-public-client (matching generated native client)
- Scope
- Product, app, credential, environment, and machine scoped.
- Request fields
- query license_key: string (required)
- query instance_id: string (required)
- query claim_id: string
- Returns
- pending_email_verification, verified with claim_activation_token/claimActivationToken, or identity_required. Inspect status/code and ok; HTTP 200 alone is not activation.
- Side effects
- Does not activate the machine. The client must call /activate after verified status.
- Notes
- HTTP 200 may mean pending_email_verification, verified or identity_required; inspect status/code and ok. Only verified returns claim_activation_token for a separate final POST /activate on the same product/serial/machine. 400 VERIFICATION_RESTART_REQUIRED means the link expired or was not sent: submit email and serial to /claim again instead of polling forever. A confirmed owner/access denial is not offline grace. Pending polling/finalization can retry transient outages without discarding the saved claim. GET is a compatibility transport; prefer POST to keep serial/proof out of URLs and logs.
Request, response and error reference
Query and header parameters
[
{
"name": "license_key",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "instance_id",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "claim_id",
"in": "query",
"required": false,
"schema": {
"type": "string"
}
}
]
HTTP 200 response (application/json)
{
"type": "object",
"properties": {
"ok": {
"type": "boolean"
},
"status": {
"type": "string"
},
"code": {
"type": "string"
},
"claim_id": {
"type": "string"
},
"claimId": {
"type": "string"
},
"emailSent": {
"type": "boolean"
},
"message": {
"type": "string"
},
"nextAction": {
"type": "string"
},
"passwordResetUrl": {
"type": "string"
},
"claimRequired": {
"type": "boolean"
},
"ownerVerificationRequired": {
"type": "boolean"
},
"requiredFields": {
"type": "array",
"items": {
"type": "string"
}
},
"claim_activation_token": {
"type": "string"
},
"claimActivationToken": {
"type": "string"
}
},
"additionalProperties": true
}
Documented failures: 400, 401, 403, 429, 500, 503.
Operation errors: VERIFICATION_RESTART_REQUIRED.
/api/license/v1/claim-status
Restore a pending serial claim and receive claim activation material after email verification.
- Auth
- x-ss-app-id + x-ss-plugin-key + x-ss-plugin-secret OR x-ss-app-id + x-ss-product-id + x-soundsync-public-client (matching generated native client)
- Scope
- Product, app, credential, environment, and machine scoped.
- Request fields
- instance_id: string (required)
- machine_id: string
- product_id: integer
- plugin_version: string
- version: string
- license_key: string (required)
- claim_id: string
- Returns
- pending_email_verification, verified with claim_activation_token/claimActivationToken, or identity_required. Inspect status/code and ok; HTTP 200 alone is not activation.
- Side effects
- Does not activate the machine. The client must call /activate after verified status.
- Notes
- HTTP 200 may mean pending_email_verification, verified or identity_required; inspect status/code and ok. Only verified returns claim_activation_token for a separate final POST /activate on the same product/serial/machine. 400 VERIFICATION_RESTART_REQUIRED means the link expired or was not sent: submit email and serial to /claim again instead of polling forever. A confirmed owner/access denial is not offline grace. Pending polling/finalization can retry transient outages without discarding the saved claim. GET is a compatibility transport; prefer POST to keep serial/proof out of URLs and logs.
Request, response and error reference
Request (application/json)
{
"type": "object",
"properties": {
"instance_id": {
"type": "string"
},
"machine_id": {
"type": "string"
},
"product_id": {
"type": "integer",
"minimum": 1
},
"plugin_version": {
"type": "string"
},
"version": {
"type": "string"
},
"license_key": {
"type": "string"
},
"claim_id": {
"type": "string"
}
},
"required": [
"license_key",
"instance_id"
],
"additionalProperties": true
}
HTTP 200 response (application/json)
{
"type": "object",
"properties": {
"ok": {
"type": "boolean"
},
"status": {
"type": "string"
},
"code": {
"type": "string"
},
"claim_id": {
"type": "string"
},
"claimId": {
"type": "string"
},
"emailSent": {
"type": "boolean"
},
"message": {
"type": "string"
},
"nextAction": {
"type": "string"
},
"passwordResetUrl": {
"type": "string"
},
"claimRequired": {
"type": "boolean"
},
"ownerVerificationRequired": {
"type": "boolean"
},
"requiredFields": {
"type": "array",
"items": {
"type": "string"
}
},
"claim_activation_token": {
"type": "string"
},
"claimActivationToken": {
"type": "string"
}
},
"additionalProperties": true
}
Documented failures: 400, 401, 403, 429, 500, 503.
Operation errors: VERIFICATION_RESTART_REQUIRED.
/api/license/v1/activate
Activate the current machine after direct serial/customer proof or a verified serial claim.
- Auth
- x-ss-app-id + x-ss-plugin-key + x-ss-plugin-secret OR x-ss-app-id + x-ss-product-id + x-soundsync-public-client (matching generated native client)
- Scope
- Product, app, credential, environment, and machine scoped.
- Request fields
- instance_id: string
- machine_id: string
- product_id: integer
- plugin_version: string
- version: string
- license_key: string
- serial: string
- email: string
- validation_token: string
- token: string
- activationId: value
- licenseReference: string
- plugin_session_id: string
- device_credential: string. Private renewal proof returned by authorized enrollment; deviceCredential is an accepted body alias. Bind to the same app/product and instance_id. It cannot independently unlock offline audio.
- license_kind: serial | email_entitlement
- device_credential_supported: boolean. Send true to request native renewal-credential enrollment. deviceCredentialSupported is an accepted body alias.
- claim_id: string
- claim_activation_token: string
- Returns
- status activated, activationId, validation_token, entitlement_id, customer, max_activations, remaining, productId, grace_expires_at, offline_grace_days.
- Side effects
- Creates or reactivates a machine activation, ensures entitlement state, marks serial redeemed/billing eligible where applicable, records events, and issues a validation token.
- Notes
- Generated native clients request device_credential_supported:true and retain the returned device_credential, plugin_session_id and newest validation_token privately. Device proof can renew after a short-lived token/session expires, but never bypasses revoked access, product/device binding or entitlement checks. device_credential_status:pending is incomplete enrollment, not an offline grant. Only an authorized signed offline_lease can support JUCE offline access.
Request, response and error reference
Request (application/json)
{
"type": "object",
"properties": {
"instance_id": {
"type": "string"
},
"machine_id": {
"type": "string"
},
"product_id": {
"type": "integer",
"minimum": 1
},
"plugin_version": {
"type": "string"
},
"version": {
"type": "string"
},
"license_key": {
"type": "string"
},
"serial": {
"type": "string"
},
"email": {
"type": "string",
"format": "email"
},
"validation_token": {
"type": "string"
},
"token": {
"type": "string"
},
"activationId": {
"oneOf": [
{
"type": "integer",
"minimum": 1
},
{
"type": "string",
"pattern": "^[1-9][0-9]*$"
}
]
},
"licenseReference": {
"type": "string"
},
"plugin_session_id": {
"type": "string"
},
"device_credential": {
"type": "string",
"description": "Private renewal proof returned by authorized enrollment; deviceCredential is an accepted body alias. Bind to the same app/product and instance_id. It cannot independently unlock offline audio."
},
"license_kind": {
"type": "string",
"enum": [
"serial",
"email_entitlement"
]
},
"device_credential_supported": {
"type": "boolean",
"description": "Send true to request native renewal-credential enrollment. deviceCredentialSupported is an accepted body alias."
},
"claim_id": {
"type": "string"
},
"claim_activation_token": {
"type": "string"
}
},
"additionalProperties": true,
"anyOf": [
{
"required": [
"instance_id"
]
},
{
"required": [
"machine_id"
]
}
]
}
HTTP 200 response (application/json)
{
"type": "object",
"properties": {
"ok": {
"type": "boolean"
},
"status": {
"type": "string"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"activationId": {
"type": "integer",
"minimum": 1
},
"validation_token": {
"type": "string"
},
"entitlement_id": {
"type": "integer",
"minimum": 1
},
"customer": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"productId": {
"type": "integer",
"minimum": 1
},
"device_credential": {
"type": "string",
"description": "Private product/device-bound renewal proof. Returned when device_credential_supported is true and enrollment succeeds. Protect it in native secure storage; it is not a portal/library token or an offline grant."
},
"device_credential_status": {
"type": "string",
"enum": [
"ready",
"pending"
],
"description": "pending means renewal enrollment is incomplete, even when activation was otherwise authorized. Follow the maintained client retry and access decision; do not manufacture offline authorization."
},
"plugin_session_id": {
"type": "string",
"description": "Private short-lived plugin license session. Distinct from durable device_credential and customer library tokens."
},
"plugin_session_expires_at": {
"type": "string",
"format": "date-time",
"description": "Expiry of plugin_session_id, not the offline grace deadline."
},
"max_activations": {
"type": "integer",
"minimum": 0
},
"remaining": {
"type": "integer",
"minimum": 0
},
"grace_expires_at": {
"type": [
"string",
"null"
]
},
"offline_grace_days": {
"type": "integer",
"minimum": 0
},
"offline_grace": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"days": {
"type": "integer",
"minimum": 0
},
"expiresAt": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true
},
"offline_lease": {
"type": "string",
"description": "Optional signed JSON envelope issued only by successful authorized activation/validation with a positive grace period and configured signer. Contains alg, kid, base64 payload and signature. Verify with trusted package public keys; decoding alone is not authorization. Never log customer grants."
}
},
"additionalProperties": true
}
Documented failures: 400, 401, 403, 500, 503.
/api/license/v1/customer-login/activate
Activate by customer account identity when the product supports account/login activation.
- Auth
- x-ss-app-id + x-ss-plugin-key + x-ss-plugin-secret OR x-ss-app-id + x-ss-product-id + x-soundsync-public-client (matching generated native client)
- Scope
- Product, app, credential, environment, and machine scoped.
- Request fields
- instance_id: string
- machine_id: string
- product_id: integer
- plugin_version: string
- version: string
- device_credential_supported: boolean. Send true to request native renewal-credential enrollment. deviceCredentialSupported is an accepted body alias.
- email: string
- password: string
- customer_token: string
- Returns
- activation result with validation token, entitlement/customer details, device limit and subscription state where applicable.
- Notes
- The token branch resolves the verified customer identity from the token. Never persist account passwords or substitute a developer token; product ownership and device checks still apply. The direct email/password compatibility branch cannot complete a two-factor challenge: it returns 401 TWO_FACTOR_REQUIRED for an enabled account. Current JUCE 0.7.3 and HISE Native 0.5.2 clients first call /api/auth/login with role:customer, continue with twoFactorToken/twoFactorCode when requested, then pass the verified customer token as customer_token here. Never substitute the challenge token or a developer session. Entitlement and machine checks still apply. Regenerate and rebuild older packages; see the dedicated customer-login guide for the complete conversation. When account-token session verification is unavailable, fail closed with 503 SESSION_VERIFICATION_UNAVAILABLE. Retry after recovery; do not treat it as authorization or confirmed revocation. A verified revoked account session returns 401 LIBRARY_SESSION_REVOKED. Shared library tokens follow their own token/ownership checks.
Request, response and error reference
Request (application/json)
{
"type": "object",
"properties": {
"instance_id": {
"type": "string"
},
"machine_id": {
"type": "string"
},
"product_id": {
"type": "integer",
"minimum": 1
},
"plugin_version": {
"type": "string"
},
"version": {
"type": "string"
},
"device_credential_supported": {
"type": "boolean",
"description": "Send true to request native renewal-credential enrollment. deviceCredentialSupported is an accepted body alias."
},
"email": {
"type": "string",
"format": "email"
},
"password": {
"type": "string"
},
"customer_token": {
"type": "string",
"minLength": 1
}
},
"additionalProperties": true,
"allOf": [
{
"anyOf": [
{
"required": [
"instance_id"
]
},
{
"required": [
"machine_id"
]
}
]
},
{
"anyOf": [
{
"required": [
"email",
"password"
]
},
{
"required": [
"customer_token"
]
}
]
}
]
}
HTTP 200 response (application/json)
{
"type": "object",
"properties": {
"ok": {
"type": "boolean"
},
"status": {
"type": "string"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"activationId": {
"type": "integer",
"minimum": 1
},
"validation_token": {
"type": "string"
},
"entitlement_id": {
"type": "integer",
"minimum": 1
},
"customer": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"productId": {
"type": "integer",
"minimum": 1
},
"device_credential": {
"type": "string",
"description": "Private product/device-bound renewal proof. Returned when device_credential_supported is true and enrollment succeeds. Protect it in native secure storage; it is not a portal/library token or an offline grant."
},
"device_credential_status": {
"type": "string",
"enum": [
"ready",
"pending"
],
"description": "pending means renewal enrollment is incomplete, even when activation was otherwise authorized. Follow the maintained client retry and access decision; do not manufacture offline authorization."
},
"plugin_session_id": {
"type": "string",
"description": "Private short-lived plugin license session. Distinct from durable device_credential and customer library tokens."
},
"plugin_session_expires_at": {
"type": "string",
"format": "date-time",
"description": "Expiry of plugin_session_id, not the offline grace deadline."
},
"max_activations": {
"type": "integer",
"minimum": 0
},
"remaining": {
"type": "integer",
"minimum": 0
},
"grace_expires_at": {
"type": [
"string",
"null"
]
},
"offline_grace_days": {
"type": "integer",
"minimum": 0
},
"offline_grace": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"days": {
"type": "integer",
"minimum": 0
},
"expiresAt": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true
},
"offline_lease": {
"type": "string",
"description": "Optional signed JSON envelope issued only by successful authorized activation/validation with a positive grace period and configured signer. Contains alg, kid, base64 payload and signature. Verify with trusted package public keys; decoding alone is not authorization. Never log customer grants."
}
},
"additionalProperties": true
}
Documented failures: 400, 401, 403, 500, 503.
Operation errors: CUSTOMER_LOGIN_INVALID, TWO_FACTOR_REQUIRED, EMAIL_VERIFICATION_REQUIRED, SESSION_VERIFICATION_UNAVAILABLE, LIBRARY_SESSION_REVOKED.
/api/license/v1/validate
Validate cached activation state on startup or during periodic revalidation.
- Auth
- x-ss-app-id + x-ss-plugin-key + x-ss-plugin-secret OR x-ss-app-id + x-ss-product-id + x-soundsync-public-client (matching generated native client)
- Scope
- Product, app, credential, environment, and machine scoped.
- Request fields
- instance_id: string
- machine_id: string
- product_id: integer
- plugin_version: string
- version: string
- license_key: string
- serial: string
- email: string
- validation_token: string
- token: string
- activationId: value
- licenseReference: string
- plugin_session_id: string
- device_credential: string. Private renewal proof returned by authorized enrollment; deviceCredential is an accepted body alias. Bind to the same app/product and instance_id. It cannot independently unlock offline audio.
- license_kind: serial | email_entitlement
- device_credential_supported: boolean. Send true to request native renewal-credential enrollment. deviceCredentialSupported is an accepted body alias.
- claim_id: string
- claim_activation_token: string
- Returns
- active/denied state, rotated validation token, last validation timestamps, offline grace fields, subscription/license state.
- Side effects
- Refreshes validation state and token when allowed. Explicit denials force lock and must not use offline grace.
- Notes
- Generated native clients request device_credential_supported:true and retain the returned device_credential, plugin_session_id and newest validation_token privately. Device proof can renew after a short-lived token/session expires, but never bypasses revoked access, product/device binding or entitlement checks. device_credential_status:pending is incomplete enrollment, not an offline grant. Only an authorized signed offline_lease can support JUCE offline access.
Request, response and error reference
Request (application/json)
{
"type": "object",
"properties": {
"instance_id": {
"type": "string"
},
"machine_id": {
"type": "string"
},
"product_id": {
"type": "integer",
"minimum": 1
},
"plugin_version": {
"type": "string"
},
"version": {
"type": "string"
},
"license_key": {
"type": "string"
},
"serial": {
"type": "string"
},
"email": {
"type": "string",
"format": "email"
},
"validation_token": {
"type": "string"
},
"token": {
"type": "string"
},
"activationId": {
"oneOf": [
{
"type": "integer",
"minimum": 1
},
{
"type": "string",
"pattern": "^[1-9][0-9]*$"
}
]
},
"licenseReference": {
"type": "string"
},
"plugin_session_id": {
"type": "string"
},
"device_credential": {
"type": "string",
"description": "Private renewal proof returned by authorized enrollment; deviceCredential is an accepted body alias. Bind to the same app/product and instance_id. It cannot independently unlock offline audio."
},
"license_kind": {
"type": "string",
"enum": [
"serial",
"email_entitlement"
]
},
"device_credential_supported": {
"type": "boolean",
"description": "Send true to request native renewal-credential enrollment. deviceCredentialSupported is an accepted body alias."
},
"claim_id": {
"type": "string"
},
"claim_activation_token": {
"type": "string"
}
},
"additionalProperties": true,
"anyOf": [
{
"required": [
"instance_id"
]
},
{
"required": [
"machine_id"
]
}
]
}
HTTP 200 response (application/json)
{
"type": "object",
"properties": {
"ok": {
"type": "boolean"
},
"status": {
"type": "string"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"activationId": {
"type": "integer",
"minimum": 1
},
"validation_token": {
"type": "string"
},
"entitlement_id": {
"type": "integer",
"minimum": 1
},
"customer": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"productId": {
"type": "integer",
"minimum": 1
},
"device_credential": {
"type": "string",
"description": "Private product/device-bound renewal proof. Returned when device_credential_supported is true and enrollment succeeds. Protect it in native secure storage; it is not a portal/library token or an offline grant."
},
"device_credential_status": {
"type": "string",
"enum": [
"ready",
"pending"
],
"description": "pending means renewal enrollment is incomplete, even when activation was otherwise authorized. Follow the maintained client retry and access decision; do not manufacture offline authorization."
},
"plugin_session_id": {
"type": "string",
"description": "Private short-lived plugin license session. Distinct from durable device_credential and customer library tokens."
},
"plugin_session_expires_at": {
"type": "string",
"format": "date-time",
"description": "Expiry of plugin_session_id, not the offline grace deadline."
},
"max_activations": {
"type": "integer",
"minimum": 0
},
"remaining": {
"type": "integer",
"minimum": 0
},
"grace_expires_at": {
"type": [
"string",
"null"
]
},
"offline_grace_days": {
"type": "integer",
"minimum": 0
},
"offline_grace": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"days": {
"type": "integer",
"minimum": 0
},
"expiresAt": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true
},
"offline_lease": {
"type": "string",
"description": "Optional signed JSON envelope issued only by successful authorized activation/validation with a positive grace period and configured signer. Contains alg, kid, base64 payload and signature. Verify with trusted package public keys; decoding alone is not authorization. Never log customer grants."
}
},
"additionalProperties": true
}
Documented failures: 400, 401, 403, 500, 503.
/api/license/v1/deactivate
Deactivate this machine and release local access.
- Auth
- x-ss-app-id + x-ss-plugin-key + x-ss-plugin-secret OR x-ss-app-id + x-ss-product-id + x-soundsync-public-client (matching generated native client)
- Scope
- Product, app, credential, environment, and machine scoped.
- Request fields
- instance_id: string
- machine_id: string
- product_id: integer
- plugin_version: string
- version: string
- license_key: string
- serial: string
- email: string
- validation_token: string
- token: string
- activationId: value
- licenseReference: string
- plugin_session_id: string
- device_credential: string. Private renewal proof returned by authorized enrollment; deviceCredential is an accepted body alias. Bind to the same app/product and instance_id. It cannot independently unlock offline audio.
- license_kind: serial | email_entitlement
- claim_id: string
- claim_activation_token: string
- deviceCredential: string
- pluginSessionId: string
- session_id: string
- sessionId: string
- soundSyncSessionId: string
- soundsync_session_id: string
- validationToken: string
- new_token: string
- newToken: string
- Returns
- status deactivated, code DEVICE_DEACTIVATED, remaining_activations.
- Side effects
- Sets deactivated_at on the activation, records an event, and should cause the client to delete local activation state.
- Notes
- Send the retained device_credential or permitted plugin_session_id with the same product and machine identity. A supported validation-token proof is a separate compatibility path: send the current validation_token with its serial/email or activation context. Product credential headers, a serial and a machine ID alone are not deactivation proof. Missing or invalid compatibility proof returns 401 deactivate_validation_token_required or deactivate_validation_token_invalid. A nonempty proof field only satisfies the request shape; the server still verifies signature, ownership, product, machine and activation generation. Confirm status/code and deactivation flags before claiming server slot release. JUCE locks locally and preserves pending proof until confirmed; deactivation does not issue a fresh offline grant.
Request, response and error reference
Request (application/json)
{
"type": "object",
"properties": {
"instance_id": {
"type": "string"
},
"machine_id": {
"type": "string"
},
"product_id": {
"type": "integer",
"minimum": 1
},
"plugin_version": {
"type": "string"
},
"version": {
"type": "string"
},
"license_key": {
"type": "string"
},
"serial": {
"type": "string"
},
"email": {
"type": "string",
"format": "email"
},
"validation_token": {
"type": "string",
"minLength": 1
},
"token": {
"type": "string",
"minLength": 1
},
"activationId": {
"oneOf": [
{
"type": "integer",
"minimum": 1
},
{
"type": "string",
"pattern": "^[1-9][0-9]*$"
}
]
},
"licenseReference": {
"type": "string"
},
"plugin_session_id": {
"type": "string",
"minLength": 1
},
"device_credential": {
"type": "string",
"description": "Private renewal proof returned by authorized enrollment; deviceCredential is an accepted body alias. Bind to the same app/product and instance_id. It cannot independently unlock offline audio.",
"minLength": 1
},
"license_kind": {
"type": "string",
"enum": [
"serial",
"email_entitlement"
]
},
"claim_id": {
"type": "string"
},
"claim_activation_token": {
"type": "string"
},
"deviceCredential": {
"type": "string",
"minLength": 1
},
"pluginSessionId": {
"type": "string",
"minLength": 1
},
"session_id": {
"type": "string",
"minLength": 1
},
"sessionId": {
"type": "string",
"minLength": 1
},
"soundSyncSessionId": {
"type": "string",
"minLength": 1
},
"soundsync_session_id": {
"type": "string",
"minLength": 1
},
"validationToken": {
"type": "string",
"minLength": 1
},
"new_token": {
"type": "string",
"minLength": 1
},
"newToken": {
"type": "string",
"minLength": 1
}
},
"additionalProperties": true,
"anyOf": [
{
"required": [
"instance_id"
]
},
{
"required": [
"machine_id"
]
}
],
"allOf": [
{
"anyOf": [
{
"required": [
"device_credential"
]
},
{
"required": [
"deviceCredential"
]
},
{
"required": [
"plugin_session_id"
]
},
{
"required": [
"pluginSessionId"
]
},
{
"required": [
"session_id"
]
},
{
"required": [
"sessionId"
]
},
{
"required": [
"soundSyncSessionId"
]
},
{
"required": [
"soundsync_session_id"
]
},
{
"required": [
"validation_token"
]
},
{
"required": [
"token"
]
},
{
"required": [
"validationToken"
]
},
{
"required": [
"new_token"
]
},
{
"required": [
"newToken"
]
}
]
}
]
}
HTTP 200 response (application/json)
{
"type": "object",
"properties": {
"ok": {
"type": "boolean"
},
"status": {
"type": "string"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"mayOpenProduct": {
"type": "boolean"
},
"activation_deactivated": {
"type": "boolean"
},
"plugin_session_deactivated": {
"type": "boolean"
},
"activation_id": {
"type": "integer",
"minimum": 1
},
"activationId": {
"type": "integer",
"minimum": 1
},
"current_activations": {
"type": "integer",
"minimum": 0
},
"max_activations": {
"type": "integer",
"minimum": 0
},
"remaining_activations": {
"type": "integer",
"minimum": 0
},
"serial_status": {
"type": "string"
}
},
"additionalProperties": true
}
Documented failures: 400, 401, 403, 404, 500, 503.
Operation errors: deactivate_validation_token_required, deactivate_validation_token_invalid, DEVICE_ACTIVATION_NOT_FOUND.
SoundSync
Related docs
Use these pages for complete plugin licensing implementation.
SoundSync
Activate an email entitlement with a code
This flow proves access to the email associated with an existing entitlement. It is separate from serial-claim verification and password-based customer-login activation.
/api/license/v1/activation/start
Begin email-code activation for a product entitlement
- Auth
- x-ss-app-id + x-ss-plugin-key + x-ss-plugin-secret OR x-ss-app-id + x-ss-product-id + x-soundsync-public-client (matching generated native client)
- Scope
- Product, customer email, and machine identity.
- Request fields
- email: string (required)
- instance_id: string (required)
- device_label: string
- plugin_version: string
- Returns
- 200: ok, status pending_email_verification, code PENDING_EMAIL_VERIFICATION; challengeId, emailSent, and branding can be present.
- Notes
- Can create a challenge and send an email. The same generic pending state can be returned when access is unavailable; it does not prove entitlement or email delivery.
Request, response and error reference
Request (application/json)
{
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email"
},
"instance_id": {
"type": "string"
},
"device_label": {
"type": "string"
},
"plugin_version": {
"type": "string"
}
},
"required": [
"email",
"instance_id"
],
"additionalProperties": true
}
HTTP 200 response (application/json)
{
"type": "object",
"properties": {
"ok": {
"type": "boolean"
},
"status": {
"const": "pending_email_verification"
},
"code": {
"const": "PENDING_EMAIL_VERIFICATION"
},
"challengeId": {
"oneOf": [
{
"type": "string",
"pattern": "^[1-9][0-9]*$"
},
{
"type": "integer",
"minimum": 1
},
{
"type": "null"
}
]
},
"emailSent": {
"type": "boolean"
},
"branding": {
"type": "object",
"properties": {},
"additionalProperties": true
}
},
"required": [
"status",
"code"
],
"additionalProperties": true
}
Documented failures: 400, 403, 500.
/api/license/v1/activation/verify-email
Verify the six-digit code and complete email-entitlement activation
- Auth
- x-ss-app-id + x-ss-plugin-key + x-ss-plugin-secret OR x-ss-app-id + x-ss-product-id + x-soundsync-public-client (matching generated native client)
- Scope
- Same product, email, and machine as the challenge.
- Request fields
- email: string (required)
- instance_id: string (required)
- device_label: string
- plugin_version: string
- device_credential_supported: boolean. Send true to request native renewal-credential enrollment. deviceCredentialSupported is an accepted body alias.
- code: string (required)
- Returns
- 200: activation result if the code and current entitlement/device checks pass.
- Notes
- Codes expire after 15 minutes and are one-use. Five incorrect attempts lock the challenge. Invalid, expired, missing, or locked codes return 400; product credential and access checks still apply.
Request, response and error reference
Request (application/json)
{
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email"
},
"instance_id": {
"type": "string"
},
"device_label": {
"type": "string"
},
"plugin_version": {
"type": "string"
},
"device_credential_supported": {
"type": "boolean",
"description": "Send true to request native renewal-credential enrollment. deviceCredentialSupported is an accepted body alias."
},
"code": {
"type": "string",
"pattern": "^\\d{6}$"
}
},
"required": [
"email",
"instance_id",
"code"
],
"additionalProperties": true
}
HTTP 200 response (application/json)
{
"type": "object",
"properties": {
"ok": {
"type": "boolean"
},
"status": {
"type": "string"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"activationId": {
"type": "integer",
"minimum": 1
},
"validation_token": {
"type": "string"
},
"entitlement_id": {
"type": "integer",
"minimum": 1
},
"customer": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"productId": {
"type": "integer",
"minimum": 1
},
"device_credential": {
"type": "string",
"description": "Private product/device-bound renewal proof. Returned when device_credential_supported is true and enrollment succeeds. Protect it in native secure storage; it is not a portal/library token or an offline grant."
},
"device_credential_status": {
"type": "string",
"enum": [
"ready",
"pending"
],
"description": "pending means renewal enrollment is incomplete, even when activation was otherwise authorized. Follow the maintained client retry and access decision; do not manufacture offline authorization."
},
"plugin_session_id": {
"type": "string",
"description": "Private short-lived plugin license session. Distinct from durable device_credential and customer library tokens."
},
"plugin_session_expires_at": {
"type": "string",
"format": "date-time",
"description": "Expiry of plugin_session_id, not the offline grace deadline."
},
"max_activations": {
"type": "integer",
"minimum": 0
},
"remaining": {
"type": "integer",
"minimum": 0
},
"grace_expires_at": {
"type": [
"string",
"null"
]
},
"offline_grace_days": {
"type": "integer",
"minimum": 0
},
"offline_grace": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"days": {
"type": "integer",
"minimum": 0
},
"expiresAt": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true
},
"offline_lease": {
"type": "string",
"description": "Optional signed JSON envelope issued only by successful authorized activation/validation with a positive grace period and configured signer. Contains alg, kid, base64 payload and signature. Verify with trusted package public keys; decoding alone is not authorization. Never log customer grants."
}
},
"additionalProperties": true
}
Documented failures: 400, 403, 500.
Keep the challenge bound to one machine
Use the same customer email, product credentials, and stable instance_id for start and verification. Enter the code into the client; do not treat the initial pending response as an unlocked plugin. After successful verification, keep the returned activation/validation state under the package policy.
Request a fresh code after expiry or a locked challenge. A serial claim link and its claim_activation_token belong to the separate claim flow; do not interchange those tokens with an email-entitlement activation code.
SoundSync
Start email-code activation
curl --fail-with-body -sS -X POST "$SOUNDSYNC_API_BASE/api/license/v1/activation/start" \
-H "x-ss-app-id: $APP_ID" \
-H "x-ss-plugin-key: $PLUGIN_KEY" \
-H "x-ss-plugin-secret: $PLUGIN_SECRET" \
-H "Content-Type: application/json" \
--data '{"email":"customer@example.com","instance_id":"example-machine"}'
SoundSync
Verify the customer-entered code
curl --fail-with-body -sS -X POST "$SOUNDSYNC_API_BASE/api/license/v1/activation/verify-email" \
-H "x-ss-app-id: $APP_ID" \
-H "x-ss-plugin-key: $PLUGIN_KEY" \
-H "x-ss-plugin-secret: $PLUGIN_SECRET" \
-H "Content-Type: application/json" \
--data '{"email":"customer@example.com","instance_id":"example-machine","code":"123456"}'
SoundSync
Native public-client request headers
| Header | Value and purpose |
|---|---|
| x-ss-app-id | The app ID from this product configuration. |
| x-ss-product-id | The positive numeric product ID from the same configuration. |
| x-soundsync-public-client | juce-native for the generated JUCE client, or hise-native for the generated native HISE client. |
SoundSync
Native startup validation request
curl --fail-with-body -sS -X POST "$SOUNDSYNC_API_BASE/api/license/v1/validate" \
-H "x-ss-app-id: $APP_ID" \
-H "x-ss-product-id: $PRODUCT_ID" \
-H "x-soundsync-public-client: juce-native" \
-H "Content-Type: application/json" \
--data '{"instance_id":"example-machine","device_credential":"DEVICE_CREDENTIAL_FROM_AUTHORIZED_RESPONSE","device_credential_supported":true}'
Keep identity and access proof separate
Replace the illustrative device credential and machine ID with values retained after authorized activation. App/product headers identify the application; they do not authorize customer access on their own. Start activation or the supported claim/customer-login flow before validation, and follow the returned denial or reactivation state.
The standard custom-HTTP and HISEScript path instead uses x-ss-app-id, x-ss-plugin-key, and x-ss-plugin-secret. Do not add those shared secrets to generated native public configuration. Use the generated client for its signed response, token/session, persistence, and audio-enforcement rules rather than treating this curl example as a replacement licensing runtime.
Enroll and retain native renewal proof
The generated JUCE client sends device_credential_supported:true during supported activation and validation. Successful enrollment returns device_credential_status:ready and a private device_credential, alongside the short-lived plugin_session_id and its plugin_session_expires_at when available. Save the newest returned proof using the maintained secure storage. The supported body aliases deviceCredentialSupported and deviceCredential map to the same fields.
A device_credential is product/device-bound renewal proof, not a customer login, library bearer token or signed offline grant. A permitted plugin_session_id or validation_token can also validate within its own lifetime; a short-lived proof is not a durable renewal strategy after a long offline period. Do not copy proof between machines or place it in URLs, logs or public screenshots.
If device_credential_status is pending, enrollment is incomplete even when another part of activation succeeded. JUCE reports device_credential_pending, stays locked and retries enrollment automatically. Do not convert this state into offline-valid access or delete the activation to obtain another slot. A completed online validation may then return a verified offline_lease for the Access period; renewal proof alone cannot unlock offline audio.
SoundSync
Native customer activation with renewal enrollment
curl --fail-with-body -sS -X POST "$SOUNDSYNC_API_BASE/api/license/v1/customer-login/activate" \
-H "x-ss-app-id: $APP_ID" \
-H "x-ss-product-id: $PRODUCT_ID" \
-H "x-soundsync-public-client: juce-native" \
-H "Content-Type: application/json" \
--data '{"instance_id":"example-machine","customer_token":"CUSTOMER_TOKEN_FROM_SUPPORTED_LOGIN","device_credential_supported":true}'
SoundSync
Native server deactivation request
curl --fail-with-body -sS -X POST "$SOUNDSYNC_API_BASE/api/license/v1/deactivate" \
-H "x-ss-app-id: $APP_ID" \
-H "x-ss-product-id: $PRODUCT_ID" \
-H "x-soundsync-public-client: juce-native" \
-H "Content-Type: application/json" \
--data '{"instance_id":"example-machine","device_credential":"DEVICE_CREDENTIAL_FROM_AUTHORIZED_RESPONSE"}'
Confirm the deactivation, not only the request
A confirmed response includes status:deactivated, code:DEVICE_DEACTIVATED and activation_deactivated:true; native session deactivation can also report plugin_session_deactivated:true and remaining_activations. No fresh offline grant is issued. Do not treat any HTTP 200 or an unconfirmed body as proof that the device slot was released.
An unavailable/expired plugin session can return 401 plugin_session_invalid on validation. Keep a valid durable device credential for renewal and follow confirmed entitlement/device denials separately. Deactivation can return 404 DEVICE_ACTIVATION_NOT_FOUND; reconcile the actual activation state rather than claiming a successful release. JUCE keeps its local pending block and protected proof until completion. These illustrative requests do not replace that runtime.
Signed offline grants in successful licensing responses
Successful product/device activation or validation may include offline_lease alongside offline_grace_days, grace_expires_at and offline_grace. offline_lease is a signed JSON envelope containing alg, kid, payload and signature; the payload and signature are base64 encoded. Use the generated JUCE verifier and its trusted public-key ring instead of trusting decoded claims alone.
The signed payload binds the issuer, product, activation, device, issue time and expiry. It is issued only for an authorized positive grace period. Disabled or zero grace, expired access, missing signing configuration and confirmed denial do not issue an offline grant. Do not log or publish a customer grant.
offline_grace.enabled and grace_expires_at describe server policy/deadlines; neither proves that a JUCE client has a valid signed grant. Require the opaque offline_lease and trusted bundled public keys for offline access. A signing-readiness problem can leave online authorization working without issuing a lease; contact SoundSync rather than fabricating offline permission. The selected period begins at successful online activation/validation and is capped by earlier access deadlines.
SoundSync
Ship the offline-capable client
JUCE update guide
Verify capability, rebuild, refresh online and distribute.
Use the serial claim helper for ownership verification
beginClaim(serial, email) starts the existing serial ownership-verification flow. Its first argument is the serial/license key, not an arbitrary web claim token. The wire request sends license_key and the current machine identity to POST /api/license/v1/claim.
A pending claim leaves the plugin locked. Keep the pending serial, email and claim ID in the native licensing flow, then use Restore or pollClaim to check /api/license/v1/claim-status. Once the server returns a claim_activation_token, the client makes the final /api/license/v1/activate request for that same serial and machine. A claim email, verified status or HTTP 200 alone does not unlock the plugin or reserve a device slot.
Pending serial/email context survives a restart in the protected cache. A temporary network failure, server error or rate limit during status checking or final activation keeps the claim pending so Restore can retry. A confirmed licensing denial is different and does not create offline permission.
SoundSync
Serial claim outcomes and recovery
| Result | What to do |
|---|---|
| pending_email_verification | Keep the product locked. Save claim_id with the serial, customer email and machine identity. Complete the private verification link, then Restore or poll claim status. |
| verified + claim_activation_token | POST /api/license/v1/activate for the same serial and machine with claim_activation_token. Verification alone is not activation or a device reservation. |
| 400 VERIFICATION_RESTART_REQUIRED | The link was not sent or has expired. Submit the email and serial to /claim again; do not poll the expired claim indefinitely. |
| 503 VERIFICATION_EMAIL_FAILED / emailSent:false | Delivery was not confirmed. Wait for service recovery and deliberately retry the claim, or contact product support. HTTP failure does not grant access. |
| SERIAL_OWNER_EMAIL_MISMATCH / SERIAL_OWNER_LOGIN_REQUIRED | Sign in to the owning customer account, verify the owner email where supported, or contact support. Do not replace the owner email or create another identity to bypass the denial. |
| identity_required / CUSTOMER_DETAILS_REQUIRED | Provide the requested identity or use the owner-login path indicated by nextAction. HTTP 200 with ok:false is not a verified claim. |
| 429, timeout or temporary server error while polling/finalizing | Maintained native clients keep a pending claim for Restore to retry. Honor retry guidance; do not clear the cache or replace the machine identity merely to retry. No offline grant is created. |
| Revoked, refunded, expired or product-mismatched serial | Stop activation and follow the purchase/support path. These are access decisions, not an eligible connectivity failure. |
