Skip to main content
Getly

API

API Documentation

Complete reference for the Getly Developer API v1

Authentication

All API requests must include your API key in the Authorization header using the Bearer token scheme.

HTTP header
Authorization: Bearer gk_your_api_key_here

Available Scopes

API keys are scoped to a specific store and have granular permissions. Create keys at /dashboard/developer/keys.

read:productswrite:productsread:ordersread:storewrite:storeread:analyticswebhooks:manageread:postswrite:postsread:couponswrite:couponsread:licenseswrite:licensescheckout:createread:billingwrite:billing

Rate Limits

API requests are rate limited to 100 requests per minute per API key. If you exceed the limit, you will receive a 429 response — wait a short moment and retry.

Plan your integrations to stay within these limits. Batch operations where possible and cache responses on your end.

Error Handling

All API responses follow a consistent format. Errors include a descriptive message.

application/json
// Success
{ "success": true, "data": { ... } }

// Error
{
  "success": false,
  "error": "Description of what went wrong",
  "errorDetail": {
    "code": "validation_failed",   // stable, safe to branch on
    "message": "price must be a positive integer in cents",
    "hint": "Fix the field named in `param` and retry the same request.",
    "param": "price",              // omitted when no single field is at fault
    "docsUrl": "https://www.getly.store/developers/docs#errors"
  }
}

HTTP Status Codes

200 Success

201 Created

400 Bad Request (invalid input)

401 Unauthorized (missing/invalid API key)

403 Forbidden (missing scope or not your resource)

404 Not Found

500 Internal Server Error

Endpoints

Base URL: https://www.getly.store. All prices are in cents (e.g. 1999 = $19.99).

GET/api/v1/productsread:products

List products for your store

bash
curl -X GET "https://www.getly.store/api/v1/products?page=1&limit=20" \
  -H "Authorization: Bearer gk_your_api_key"
Response · application/json
{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "name": "Premium Icon Pack",
      "slug": "premium-icon-pack-abc123",
      "price": 1999,
      "status": "active",
      "category": { "id": "uuid", "name": "Design", "slug": "design" },
      "images": [{ "url": "...", "altText": "..." }]
    }
  ],
  "total": 42,
  "page": 1,
  "limit": 20,
  "hasMore": true
}
POST/api/v1/productswrite:products

Create a new product in your store

bash
curl -X POST "https://www.getly.store/api/v1/products" \
  -H "Authorization: Bearer gk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My New Product",
    "description": "A great digital product",
    "price": 29.99,
    "categoryId": "uuid",
    "status": "active",
    "tags": ["design", "icons"],
    "images": [{ "url": "https://cdn.getly.store/...png", "altText": "cover" }],
    "files": [{ "fileUrl": "https://cdn.getly.store/files/.../abc.zip", "fileName": "pack.zip", "fileSize": 10485760, "fileType": "application/zip" }]
  }'
Response · application/json
{
  "success": true,
  "data": {
    "id": "uuid",
    "name": "My New Product",
    "slug": "my-new-product-abc123",
    "price": 2999,
    "status": "active",
    "createdAt": "2025-01-15T10:30:00Z"
  }
}
GET/api/v1/products/:idread:products

Get product details by ID

bash
curl -X GET "https://www.getly.store/api/v1/products/{id}" \
  -H "Authorization: Bearer gk_your_api_key"
Response · application/json
{
  "success": true,
  "data": {
    "id": "uuid",
    "name": "Premium Icon Pack",
    "price": 1999,
    "images": [...],
    "files": [...],
    "reviews": [...]
  }
}
PATCH/api/v1/products/:idwrite:products

Update an existing product

bash
curl -X PATCH "https://www.getly.store/api/v1/products/{id}" \
  -H "Authorization: Bearer gk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Updated Name", "price": 39.99 }'
Response · application/json
{
  "success": true,
  "data": { "id": "uuid", "name": "Updated Name", "price": 3999 }
}
DELETE/api/v1/products/:idwrite:products

Archive (soft-delete) a product. It stops being publicly listed; existing orders and downloads keep working.

bash
curl -X DELETE "https://www.getly.store/api/v1/products/{id}" \
  -H "Authorization: Bearer gk_your_api_key"
Response · application/json
{
  "success": true,
  "data": { "id": "uuid", "status": "archived" },
  "message": "Product archived"
}
POST/api/v1/products/:id/fileswrite:products

Attach a downloadable file to a product. Use multipart for small files, or the presign sub-route + a direct R2 PUT for files up to 2GB. You can also pass a files[] array to POST /api/v1/products to attach at creation time.

bash
# Direct upload (multipart) — best for files under ~4MB:
curl -X POST "https://www.getly.store/api/v1/products/{id}/files" \
  -H "Authorization: Bearer gk_your_api_key" \
  -F "file=@/path/to/your-file.zip"

# Large files (up to 2GB) — 3 steps:
# 1) get a presigned upload URL
curl -X POST "https://www.getly.store/api/v1/products/{id}/files/presign" \
  -H "Authorization: Bearer gk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "fileName": "pack.zip", "fileSize": 524288000, "fileType": "application/zip" }'
# 2) PUT the bytes straight to R2 (use the returned uploadUrl)
curl -X PUT "<uploadUrl>" --data-binary "@/path/to/pack.zip" \
  -H "Content-Length: 524288000"
# 3) attach the uploaded object (use the returned fileUrl)
curl -X POST "https://www.getly.store/api/v1/products/{id}/files" \
  -H "Authorization: Bearer gk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "fileUrl": "<fileUrl>", "fileName": "pack.zip", "fileSize": 524288000, "fileType": "application/zip" }'
Response · application/json
{
  "success": true,
  "data": {
    "id": "uuid",
    "fileName": "pack.zip",
    "fileUrl": "https://cdn.getly.store/files/.../abc.zip",
    "fileSize": 524288000,
    "fileType": "application/zip",
    "version": "1.0",
    "isLatest": true,
    "createdAt": "2026-06-02T10:30:00Z"
  }
}
POST/api/v1/uploads/images/presignwrite:products

Get a presigned URL to upload a product image (max 10MB; png, jpeg, webp, gif, avif). Required before a product can go live.

bash
# Images are uploaded in 3 steps — there is no direct multipart route.
# 1) ask for a presigned URL (max 10MB; png, jpeg, webp, gif or avif)
curl -X POST "https://www.getly.store/api/v1/uploads/images/presign" \
  -H "Authorization: Bearer gk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "fileName": "cover.png", "fileSize": 184320, "contentType": "image/png" }'

# 2) PUT the raw bytes to the returned uploadUrl (expires in 1h).
#    Content-Length MUST equal the fileSize you declared.
curl -X PUT "<uploadUrl>" --data-binary "@/path/to/cover.png" \
  -H "Content-Type: image/png" -H "Content-Length: 184320"

# 3) reference the returned publicUrl when you create or update the product.
#    An image is REQUIRED before a product can go live.
curl -X PATCH "https://www.getly.store/api/v1/products/{id}" \
  -H "Authorization: Bearer gk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "images": ["<publicUrl>"] }'
Response · application/json
{
  "success": true,
  "data": {
    "uploadUrl": "https://<bucket>.r2.cloudflarestorage.com/...",
    "publicUrl": "https://cdn.getly.store/images/.../abc.png",
    "expiresIn": 3600
  }
}
GET/api/v1/ordersread:orders

List orders containing items from your store

bash
curl -X GET "https://www.getly.store/api/v1/orders?page=1&limit=20" \
  -H "Authorization: Bearer gk_your_api_key"
Response · application/json
{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "orderId": "uuid",
      "price": 2999,
      "order": { "status": "completed", "buyer": { "name": "John" } },
      "product": { "name": "Premium Pack" }
    }
  ],
  "total": 156,
  "page": 1
}
GET/api/v1/orders/:idread:orders

Get order details (only items from your store)

bash
curl -X GET "https://www.getly.store/api/v1/orders/{id}" \
  -H "Authorization: Bearer gk_your_api_key"
Response · application/json
{
  "success": true,
  "data": {
    "id": "uuid",
    "status": "completed",
    "total": 5999,
    "buyer": { "name": "John", "email": "john@example.com" },
    "items": [...]
  }
}
GET/api/v1/storeread:store

Get your store information

bash
curl -X GET "https://www.getly.store/api/v1/store" \
  -H "Authorization: Bearer gk_your_api_key"
Response · application/json
{
  "success": true,
  "data": {
    "id": "uuid",
    "name": "My Store",
    "slug": "my-store",
    "description": "...",
    "totalSales": 250,
    "totalRevenue": 450000,
    "badges": [...]
  }
}
PATCH/api/v1/storewrite:store

Update store name, description, website, socialLinks

bash
curl -X PATCH "https://www.getly.store/api/v1/store" \
  -H "Authorization: Bearer gk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "description": "Updated store description" }'
Response · application/json
{
  "success": true,
  "data": { "id": "uuid", "name": "My Store", "description": "Updated store description" }
}
GET/api/v1/analyticsread:analytics

Get sales analytics for your store

bash
curl -X GET "https://www.getly.store/api/v1/analytics" \
  -H "Authorization: Bearer gk_your_api_key"
Response · application/json
{
  "success": true,
  "data": {
    "totalSales": 250,
    "totalRevenue": 450000,
    "monthlySales": 42,
    "monthlyRevenue": 75000,
    "averageOrderValue": 1800,
    "productCount": 15,
    "totalDownloads": 1200,
    "salesByMonth": [
      { "month": "2025-01", "sales": 38, "revenue": 68000 }
    ]
  }
}
GET/api/v1/webhookswebhooks:manage

List webhook endpoints for your store

bash
curl -X GET "https://www.getly.store/api/v1/webhooks" \
  -H "Authorization: Bearer gk_your_api_key"
Response · application/json
{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "url": "https://example.com/webhooks",
      "events": ["sale.completed"],
      "isActive": true
    }
  ]
}
POST/api/v1/webhookswebhooks:manage

Register a new webhook endpoint

bash
curl -X POST "https://www.getly.store/api/v1/webhooks" \
  -H "Authorization: Bearer gk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks",
    "events": ["sale.completed", "product.updated"]
  }'
Response · application/json
{
  "success": true,
  "data": {
    "id": "uuid",
    "url": "https://example.com/webhooks",
    "events": ["sale.completed", "product.updated"],
    "secret": "abcdef1234567890..."
  }
}
POST/api/v1/billing/planswrite:billing

Create a recurring plan for your OWN product (Getly Billing). amount in integer cents (min 50), intervalUnit day/week/month/year, intervalCount 1–52. Requires an approved Billing application — 403 billing_not_approved until then. GET lists plans; GET/PATCH /{id} reads and edits one.

bash
curl -X POST "https://www.getly.store/api/v1/billing/plans" \
  -H "Authorization: Bearer gk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Pro", "amount": 1900, "intervalUnit": "month", "externalId": "pro-monthly" }'
Response · application/json
{
  "success": true,
  "data": {
    "id": "uuid",
    "name": "Pro",
    "amount": 1900,
    "currency": "usd",
    "interval": { "unit": "month", "count": 1 },
    "externalId": "pro-monthly",
    "isActive": true
  }
}
POST/api/v1/billing/checkoutwrite:billing

Mint a hosted subscribe page for one customer, valid 24 hours. customerRef is your own id for the customer and comes back on every billing.* webhook as externalCustomerRef. The customer pays by card (renews by itself) or crypto (one period at a time).

bash
curl -X POST "https://www.getly.store/api/v1/billing/checkout" \
  -H "Authorization: Bearer gk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "planId": "uuid", "customerRef": "user_8841",
        "successUrl": "https://your-app.com/welcome",
        "cancelUrl": "https://your-app.com/pricing" }'
Response · application/json
{
  "success": true,
  "data": {
    "url": "https://www.getly.store/billing/subscribe/uuid?t=…",
    "expiresAt": "2026-09-09T09:00:00.000Z"
  }
}
GET/api/v1/billing/subscriptions/{id}read:billing

The status check your backend makes before granting access. status: active, past_due (card renewal failed, Stripe retries), canceled, expired (crypto period ran out). Treat active and past_due as entitled until currentPeriodEnd. The list endpoint filters by status, customerRef and planId.

bash
curl "https://www.getly.store/api/v1/billing/subscriptions/uuid" \
  -H "Authorization: Bearer gk_your_api_key"
Response · application/json
{
  "success": true,
  "data": {
    "id": "uuid",
    "customerRef": "user_8841",
    "status": "active",
    "paymentMethod": "stripe",
    "currentPeriodEnd": "2026-10-08T09:14:00.000Z",
    "cancelAtPeriodEnd": false,
    "plan": { "name": "Pro", "amount": 1900, "interval": { "unit": "month", "count": 1 } }
  }
}
POST/api/v1/billing/subscriptions/{id}/cancelwrite:billing

Stop renewal at the end of the paid period — never an immediate cut-off. You receive billing.subscription.canceled when it ends (card) or billing.subscription.expired (crypto). 409 subscription_not_cancellable when already ended.

bash
curl -X POST "https://www.getly.store/api/v1/billing/subscriptions/uuid/cancel" \
  -H "Authorization: Bearer gk_your_api_key"
Response · application/json
{
  "success": true,
  "data": { "id": "uuid", "status": "active", "cancelAtPeriodEnd": true, "currentPeriodEnd": "2026-10-08T09:14:00.000Z" }
}

Getly Billing

Recurring plans for a product you run yourself — a SaaS, a community, a tool. Plans live in the API, the subscribe page lives on getly.store, and your backend grants access from five webhooks that all carry your own customer reference.

Access is by application: tell us the website and what you sell at /dashboard/billing. Until approval, write calls answer 403 billing_not_approved and read calls work.

  • billing.subscription.created
  • billing.subscription.renewed
  • billing.payment_failed
  • billing.subscription.canceled
  • billing.subscription.expired
How Getly Billing works

Webhooks

Webhooks allow your application to receive real-time notifications when events happen in your store. Register endpoints at /dashboard/developer/webhooks or via the API.

Event Types

sale.completed A sale was completed for one of your products

product.created A product was created in your store

product.updated A product in your store was updated

review.created A new review was left on one of your products

download.completed A buyer downloaded one of your products

refund.created A buyer requested a refund (the decision comes later as order.refunded)

order.refunded A refund was booked on one of your orders

dispute.created A buyer opened a dispute

dispute.resolved A dispute was resolved

checkout_link.completed A checkout link was paid

license.activated A license key was activated

access.expiring A timed-access purchase expires in 7 days

access.expired A timed-access purchase expired

billing.subscription.created Getly Billing: a customer subscribed to one of your plans

billing.subscription.renewed Getly Billing: a subscription renewed and was paid

billing.payment_failed Getly Billing: a renewal payment failed

billing.subscription.canceled Getly Billing: a subscription was canceled

billing.subscription.expired Getly Billing: a crypto-paid subscription expired unpaid

* Subscribe to all events

Payload Format

Every webhook delivery sends a JSON POST request with the following structure:

POST · application/json
POST https://your-endpoint.com/webhooks
Content-Type: application/json
X-Getly-Signature: sha256=<hmac_hex>
X-Getly-Event: sale.completed

{
  "event": "sale.completed",
  "deliveryId": "uuid",
  "data": {
    "orderId": "uuid",
    "productId": "uuid",
    "buyerEmail": "john@example.com",
    "amount": 2999
  },
  "timestamp": "2025-01-15T10:30:00.000Z"
}

Signature Verification

Every webhook delivery includes an X-Getly-Signature header. Verify the signature using HMAC-SHA256 with your webhook secret to ensure the request came from Getly.

node.js
const crypto = require('crypto');

function verifyWebhook(body, signature, secret) {
  const expected = 'sha256=' +
    crypto
      .createHmac('sha256', secret)
      .update(body)
      .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// In your webhook handler
app.post('/webhooks', (req, res) => {
  const signature = req.headers['x-getly-signature'];
  const isValid = verifyWebhook(
    JSON.stringify(req.body),
    signature,
    process.env.GETLY_WEBHOOK_SECRET
  );

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  const { event, data } = req.body;
  // Handle the event...

  res.status(200).send('OK');
});

Retry Policy

Failed deliveries (non-2xx response or timeout) are retried automatically with exponential backoff:

Attempt 1: Retry after 1 minute

Attempt 2: Retry after 5 minutes

Attempt 3: Retry after 30 minutes

Attempt 4: Retry after 2 hours

Attempt 5: Give up (delivery marked as failed)

Your endpoint should respond within 10 seconds. Return a 2xx status code to acknowledge receipt.