Developer Docs
Storefront API
API References
Trial

Trial

Read the trial status of a single subscription contract. This endpoint is read-only and carries no PII.

Endpoint

GET /subscriptions/{contractId}/trial

All paths are relative to the base URL https://<your-app-domain>/storefront-api/v1.

Authentication

Like every Storefront API endpoint (except /__health), this requires both auth layers:

X-Joy-App-Id: <app-id>
X-Joy-Secret-Key: sk_live_xxx     # server-side only
X-Session-Id: <session-id>

The shopId is always derived from the session. shopId is never read from a header.

Path parameters

NameTypeRequiredNotes
contractIdnumberyesThe numeric subscriptionContractId.

Note: contractId is validated inline in the controller. A non-numeric or missing id returns 422 VALIDATION_FAILED — this is the only endpoint that uses 422.

A contract that does not exist, or that is not owned by the authenticated customer, returns 404 NOT_FOUND (ownership is enforced to prevent IDOR; existence is not leaked).

Request

curl -s "https://<your-app-domain>/storefront-api/v1/subscriptions/123456789/trial" \
  -H "X-Joy-App-Id: <app-id>" \
  -H "X-Joy-Secret-Key: sk_live_xxx" \
  -H "X-Session-Id: <session-id>"

Response

{
  "success": true,
  "data": {
    "contractId": 123456789,
    "hasTrial": true,
    "isInTrial": true,
    "trialEndsAt": "2026-07-10T00:00:00.000Z",
    "daysRemaining": 16,
    "trialProcessed": false,
    "afterAction": "continue",
    "status": "ACTIVE"
  }
}

Response fields

FieldTypeDescription
contractIdnumberThe numeric subscriptionContractId.
hasTrialbooleanWhether the contract has a trial configured.
isInTrialbooleanWhether the contract is currently within its trial window.
trialEndsAtstring | nullISO 8601 timestamp when the trial ends.
daysRemainingnumber | nullDays remaining in the trial. null when the contract has no trial (hasTrial is false).
trialProcessedbooleanWhether the trial transition has already been processed.
afterActionstring | nullWhat happens when the trial ends: pause | continue | null.
statusstringContract status: ACTIVE | PAUSED | CANCELLED | EXPIRED | FAILED | PENDING | DECLINED.

Errors

This endpoint uses the standard error envelope:

{ "success": false, "error": { "code": "NOT_FOUND", "message": "..." } }
HTTPCodeWhen
401INVALID_APP_CREDENTIALSApp ID / Secret Key missing or invalid.
401AUTH_REQUIREDSession header missing.
401SESSION_EXPIREDSession is no longer valid.
403SHOP_MISMATCHApp shop and session shop differ.
404NOT_FOUNDContract not found or not owned by the customer.
422VALIDATION_FAILEDcontractId is non-numeric or missing.
429RATE_LIMITEDA rate-limit dimension was exceeded.
500INTERNAL_ERRORUnexpected server error (generic message).

Rate limits

This is a read (GET) request, so it counts against the ip, customer, and shop dimensions (not the write dimension). Exceeding any one of them returns 429 RATE_LIMITED.

DimensionLimitWindow (s)BurstScope
ip1206020Per IP (per-shop after auth).
customer606010Per authenticated customer.
shop6006060Per shop.

On a 429, the response includes the headers Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset, plus retryAfterSeconds and scope in the body. Integrate against the error code, scope, and retryAfterSeconds rather than the human-readable message.

Related webhooks

Trial transitions are also emitted as outbound webhooks (Joy to your endpoint) from the trial cron job. The topics are custom to Joy:

TopicFired when
trial/endingA trial is approaching its end.
trial/endedA trial has ended.
Product
Install on ShopifyWebsitePricing
Resources
DocumentationGetting StartedFAQsIntegrations
Company
Avada GroupContact Support
© 2026 Avada Group. All rights reserved.