Access Groups

Gate digital files and pages behind product purchases, subscriptions, or manual grants

GET /admin/api/access-groups

List every access group ordered by name. Each row includes aggregate counts: files attached, pages attached, granting products, and unique members with active entitlement.

POST /admin/api/access-groups

Create a new access group. Slug must be 1–64 characters, lowercase alphanumerics and hyphens only, with no leading or trailing hyphen, and unique within the shop.

GET /admin/api/access-groups/{id}

Fetch full detail for a single access group, including the attached files, pages, granting products, and per-source member rows.

PATCH /admin/api/access-groups/{id}

Update an access group's slug, name, and description. All three fields are sent on every call; the new slug must remain unique.

DELETE /admin/api/access-groups/{id}

Permanently delete an access group. All file, page, product-assignment, and manual-grant linkages are removed via cascade. Customers lose any entitlement that was derived solely from this group.

PUT /admin/api/access-groups/{id}/files

Replace the full set of digital files attached to a group. The `file_ids` array becomes the exact membership after the call — files not listed are removed, and an empty array clears the group. The call is idempotent.

PUT /admin/api/access-groups/{id}/pages

Replace the full set of custom pages attached to a group. The `page_ids` array becomes the exact membership after the call — pages not listed are detached, and an empty array clears the group. The call is idempotent.

GET /admin/api/access-groups/{id}/members

List all current members of a group with one row per (customer × entitlement source). A customer entitled via both an order and a manual grant appears twice, once per source. `source` is one of "order", "subscription", or "manual_grant". Expired manual grants are still returned so admins can see and revoke them.

POST /admin/api/access-groups/{id}/grants

Manually grant a customer access to a group. The created grant is independent of any product purchase or subscription — use it for comps, gifts, or migrating customers from another platform. The admin who created the grant is recorded as `granted_by`.

DELETE /admin/api/access-grants/{grant_id}

Revoke a manual access grant by its grant ID. Only affects manual grants — order- and subscription-based access cannot be revoked through this endpoint. Note the path uses `access-grants`, not `access-groups`.

GET /admin/api/products/{id}/access-groups

List the access groups a product grants on purchase, enriched with each group's slug and name plus the configured access duration.

PUT /admin/api/products/{id}/access-groups

Replace the full set of access-group assignments for a product. The `assignments` array becomes the exact set after the call — groups not listed are detached. Each assignment can set `duration_days` for time-limited access, or null for lifetime. Send `{"assignments": []}` to remove all groups from the product.

GET /admin/api/access-groups

List every access group ordered by name. Each row includes aggregate counts: files attached, pages attached, granting products, and unique members with active entitlement.

Response 200

[
  {
    "id": 1,
    "slug": "premium",
    "name": "Premium Content",
    "description": "Bonus material for premium-tier buyers.",
    "file_count": 4,
    "page_count": 2,
    "granting_product_count": 3,
    "member_count": 128,
    "created_at": "2026-03-10T09:00:00Z",
    "updated_at": "2026-04-22T14:30:00Z"
  },
  {
    "id": 2,
    "slug": "course-advanced",
    "name": "Advanced Course",
    "description": null,
    "file_count": 12,
    "page_count": 6,
    "granting_product_count": 1,
    "member_count": 47,
    "created_at": "2026-03-15T10:00:00Z",
    "updated_at": "2026-03-15T10:00:00Z"
  }
]

Example

curl "https://yourshop.zeroshop.io/admin/api/access-groups" \
  -H "Authorization: Bearer zspat_..."
POST /admin/api/access-groups

Create a new access group. Slug must be 1–64 characters, lowercase alphanumerics and hyphens only, with no leading or trailing hyphen, and unique within the shop.

Request Body

{
  "slug": "premium",
  "name": "Premium Content",
  "description": "Bonus material for premium-tier buyers."
}

Response 201

{
  "id": 1,
  "slug": "premium",
  "name": "Premium Content",
  "description": "Bonus material for premium-tier buyers.",
  "file_count": 0,
  "page_count": 0,
  "granting_product_count": 0,
  "member_count": 0,
  "created_at": "2026-05-13T10:00:00Z",
  "updated_at": "2026-05-13T10:00:00Z"
}

Example

curl -X POST "https://yourshop.zeroshop.io/admin/api/access-groups" \
  -H "Authorization: Bearer zspat_..." \
  -H "Content-Type: application/json" \
  -d '{"slug":"premium","name":"Premium Content","description":"Bonus material for premium-tier buyers."}'
GET /admin/api/access-groups/{id}

Fetch full detail for a single access group, including the attached files, pages, granting products, and per-source member rows.

Response 200

{
  "id": 1,
  "slug": "premium",
  "name": "Premium Content",
  "description": "Bonus material for premium-tier buyers.",
  "file_count": 2,
  "page_count": 1,
  "granting_product_count": 1,
  "member_count": 2,
  "created_at": "2026-03-10T09:00:00Z",
  "updated_at": "2026-04-22T14:30:00Z",
  "files": [
    {
      "id": 11,
      "original_filename": "premium-handbook.pdf",
      "file_size": 1048576,
      "content_type": "application/pdf",
      "storage_key": "tenants/42/files/abc123.pdf",
      "sort_order": 0,
      "created_at": "2026-03-11T08:00:00Z"
    },
    {
      "id": 12,
      "original_filename": "bonus-tracks.zip",
      "file_size": 8388608,
      "content_type": "application/zip",
      "storage_key": "tenants/42/files/def456.zip",
      "sort_order": 1,
      "created_at": "2026-03-12T08:00:00Z"
    }
  ],
  "pages": [
    {
      "id": 5,
      "slug": "premium-welcome",
      "title": "Welcome, Premium Member"
    }
  ],
  "granting_products": [
    {
      "product_id": 17,
      "product_slug": "premium-bundle",
      "product_name": "Premium Bundle",
      "duration_days": null,
      "is_subscription": false
    }
  ],
  "members": [
    {
      "customer_id": 301,
      "customer_email": "alice@example.com",
      "customer_name": "Alice Schmidt",
      "source": "order",
      "source_id": 9001,
      "expires_at": null,
      "note": null,
      "group_name": "Premium Content",
      "group_slug": "premium"
    },
    {
      "customer_id": 302,
      "customer_email": "bob@example.com",
      "customer_name": null,
      "source": "manual_grant",
      "source_id": 14,
      "expires_at": "2026-12-31T23:59:59Z",
      "note": "Comp for support issue #482",
      "group_name": "Premium Content",
      "group_slug": "premium"
    }
  ]
}

Example

curl "https://yourshop.zeroshop.io/admin/api/access-groups/1" \
  -H "Authorization: Bearer zspat_..."
PATCH /admin/api/access-groups/{id}

Update an access group's slug, name, and description. All three fields are sent on every call; the new slug must remain unique.

Request Body

{
  "slug": "premium-plus",
  "name": "Premium Plus",
  "description": "Premium content with extra bonus tracks."
}

Response 200

{
  "id": 1,
  "slug": "premium-plus",
  "name": "Premium Plus",
  "description": "Premium content with extra bonus tracks.",
  "file_count": 2,
  "page_count": 1,
  "granting_product_count": 1,
  "member_count": 128,
  "created_at": "2026-03-10T09:00:00Z",
  "updated_at": "2026-05-13T10:05:00Z"
}

Example

curl -X PATCH "https://yourshop.zeroshop.io/admin/api/access-groups/1" \
  -H "Authorization: Bearer zspat_..." \
  -H "Content-Type: application/json" \
  -d '{"slug":"premium-plus","name":"Premium Plus","description":"Premium content with extra bonus tracks."}'
DELETE /admin/api/access-groups/{id}

Permanently delete an access group. All file, page, product-assignment, and manual-grant linkages are removed via cascade. Customers lose any entitlement that was derived solely from this group.

Response 204

Example

curl -X DELETE "https://yourshop.zeroshop.io/admin/api/access-groups/1" \
  -H "Authorization: Bearer zspat_..."
PUT /admin/api/access-groups/{id}/files

Replace the full set of digital files attached to a group. The `file_ids` array becomes the exact membership after the call — files not listed are removed, and an empty array clears the group. The call is idempotent.

Request Body

{
  "file_ids": [
    11,
    12,
    15
  ]
}

Response 200

{
  "id": 1,
  "slug": "premium",
  "name": "Premium Content",
  "description": "Bonus material for premium-tier buyers.",
  "file_count": 3,
  "page_count": 1,
  "granting_product_count": 1,
  "member_count": 128,
  "created_at": "2026-03-10T09:00:00Z",
  "updated_at": "2026-05-13T10:10:00Z"
}

Example

curl -X PUT "https://yourshop.zeroshop.io/admin/api/access-groups/1/files" \
  -H "Authorization: Bearer zspat_..." \
  -H "Content-Type: application/json" \
  -d '{"file_ids":[11,12,15]}'
PUT /admin/api/access-groups/{id}/pages

Replace the full set of custom pages attached to a group. The `page_ids` array becomes the exact membership after the call — pages not listed are detached, and an empty array clears the group. The call is idempotent.

Request Body

{
  "page_ids": [
    5,
    7
  ]
}

Response 200

{
  "id": 1,
  "slug": "premium",
  "name": "Premium Content",
  "description": "Bonus material for premium-tier buyers.",
  "file_count": 3,
  "page_count": 2,
  "granting_product_count": 1,
  "member_count": 128,
  "created_at": "2026-03-10T09:00:00Z",
  "updated_at": "2026-05-13T10:11:00Z"
}

Example

curl -X PUT "https://yourshop.zeroshop.io/admin/api/access-groups/1/pages" \
  -H "Authorization: Bearer zspat_..." \
  -H "Content-Type: application/json" \
  -d '{"page_ids":[5,7]}'
GET /admin/api/access-groups/{id}/members

List all current members of a group with one row per (customer × entitlement source). A customer entitled via both an order and a manual grant appears twice, once per source. `source` is one of "order", "subscription", or "manual_grant". Expired manual grants are still returned so admins can see and revoke them.

Response 200

[
  {
    "customer_id": 301,
    "customer_email": "alice@example.com",
    "customer_name": "Alice Schmidt",
    "source": "order",
    "source_id": 9001,
    "expires_at": null,
    "note": null,
    "group_name": "Premium Content",
    "group_slug": "premium"
  },
  {
    "customer_id": 301,
    "customer_email": "alice@example.com",
    "customer_name": "Alice Schmidt",
    "source": "manual_grant",
    "source_id": 14,
    "expires_at": "2026-12-31T23:59:59Z",
    "note": "Promo from launch event",
    "group_name": "Premium Content",
    "group_slug": "premium"
  },
  {
    "customer_id": 302,
    "customer_email": "bob@example.com",
    "customer_name": null,
    "source": "subscription",
    "source_id": 55,
    "expires_at": null,
    "note": null,
    "group_name": "Premium Content",
    "group_slug": "premium"
  }
]

Example

curl "https://yourshop.zeroshop.io/admin/api/access-groups/1/members" \
  -H "Authorization: Bearer zspat_..."
POST /admin/api/access-groups/{id}/grants

Manually grant a customer access to a group. The created grant is independent of any product purchase or subscription — use it for comps, gifts, or migrating customers from another platform. The admin who created the grant is recorded as `granted_by`.

Request Body

{
  "customer_id": 302,
  "expires_at": "2026-12-31T23:59:59Z",
  "note": "Comp for support issue #482"
}

Response 201

{
  "id": 14,
  "customer_id": 302,
  "group_id": 1,
  "expires_at": "2026-12-31T23:59:59Z",
  "granted_by": 4,
  "note": "Comp for support issue #482",
  "created_at": "2026-05-13T10:15:00Z"
}

Example

curl -X POST "https://yourshop.zeroshop.io/admin/api/access-groups/1/grants" \
  -H "Authorization: Bearer zspat_..." \
  -H "Content-Type: application/json" \
  -d '{"customer_id":302,"expires_at":"2026-12-31T23:59:59Z","note":"Comp for support issue #482"}'
DELETE /admin/api/access-grants/{grant_id}

Revoke a manual access grant by its grant ID. Only affects manual grants — order- and subscription-based access cannot be revoked through this endpoint. Note the path uses `access-grants`, not `access-groups`.

Response 204

Example

curl -X DELETE "https://yourshop.zeroshop.io/admin/api/access-grants/14" \
  -H "Authorization: Bearer zspat_..."
GET /admin/api/products/{id}/access-groups

List the access groups a product grants on purchase, enriched with each group's slug and name plus the configured access duration.

Response 200

[
  {
    "group_id": 1,
    "slug": "premium",
    "name": "Premium Content",
    "duration_days": null
  },
  {
    "group_id": 2,
    "slug": "course-advanced",
    "name": "Advanced Course",
    "duration_days": 365
  }
]

Example

curl "https://yourshop.zeroshop.io/admin/api/products/17/access-groups" \
  -H "Authorization: Bearer zspat_..."
PUT /admin/api/products/{id}/access-groups

Replace the full set of access-group assignments for a product. The `assignments` array becomes the exact set after the call — groups not listed are detached. Each assignment can set `duration_days` for time-limited access, or null for lifetime. Send `{"assignments": []}` to remove all groups from the product.

Request Body

{
  "assignments": [
    {
      "group_id": 1,
      "duration_days": null
    },
    {
      "group_id": 2,
      "duration_days": 365
    }
  ]
}

Response 200

[
  {
    "group_id": 1,
    "slug": "premium",
    "name": "Premium Content",
    "duration_days": null
  },
  {
    "group_id": 2,
    "slug": "course-advanced",
    "name": "Advanced Course",
    "duration_days": 365
  }
]

Example

curl -X PUT "https://yourshop.zeroshop.io/admin/api/products/17/access-groups" \
  -H "Authorization: Bearer zspat_..." \
  -H "Content-Type: application/json" \
  -d '{"assignments":[{"group_id":1,"duration_days":null},{"group_id":2,"duration_days":365}]}'

We value your privacy

We use cookies for essential site functionality and, with your consent, analytics to understand how our platform is used. No personal data is shared with third parties. See our Privacy Policy for details.