API Reference
Access Group
Endpoints for public and member access groups and related access settings.
Related In Learn
Access Overview
Control who can use your tools with public and member access groups.
Access group endpoints manage public, member, and invite-only access groups and related access settings.
Endpoints
GET /studio/access-group/list
List every access group in the current studio using the workspace tied to your API key.
- Full URL:
https://api.pickaxe.co/v1/studio/access-group/list
Request Fields
This endpoint does not define request body fields.
Examples
curl -X GET https://api.pickaxe.co/v1/studio/access-group/list \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN"GET /studio/product/list
Legacy access-group listing endpoint retained for backwards compatibility.
- Full URL:
https://api.pickaxe.co/v1/studio/product/list - Status: Deprecated
Request Fields
This endpoint does not define request body fields.
Examples
curl -X GET https://api.pickaxe.co/v1/studio/product/list \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN"POST /studio/access-group/create
Create a new access group with limit, upgrade-flow, and card-display configuration.
- Full URL:
https://api.pickaxe.co/v1/studio/access-group/create
Request Fields
data(object, required): The access-group payload to create. The API also accepts the same fields at the top level, but using data keeps the request format consistent with the rest of the Studio API.type("public" | "members" | "invite_only", required): The access-group type. Public groups are meant for non-members, members groups are intended for signed-in or paid members, and invite_only groups are restricted to manually invited users. If the studio visibility isinvite-only, this must beinvite_only.name(string, optional): Internal group name used in the workspace UI and administrative flows. Must be 1 to 60 characters when supplied. Defaults to"My New Access Group".description(string, optional): Internal description for the access group. Limited to 100 characters. Defaults to"".image(string | null, optional): Optional image URL used when the group card displays an image. Defaults tonull.displayName(string | null, optional): Optional public-facing card title. If omitted, clients typically fall back to name. Defaults tonull.displayDescription(string | null, optional): Optional public-facing card description. If omitted, clients typically fall back to description. Defaults tonull.limit(number, optional): Usage limit for the group. Use -1337 for unlimited access. Finite limits are typically paired with limitInterval. Defaults to-1337.limitInterval("day" | "week" | "month" | "year" | "one_time" | null, optional): The reset period for a finite limit. Use one_time for a non-recurring wallet or usage allocation. Defaults tonull.trialPeriodDays(number | null, optional): Positive number of trial days. This cannot be set forinvite_onlyaccess groups. Defaults tonull.upgrade(object, optional): Controls the experience shown after a user reaches their usage limit. Defaults to{...default upgrade payload}.limitMessage(string, optional): The message shown when a user reaches the access limit for this group. Defaults to"Please upgrade to continue".limitCTA(string, optional): The CTA label used when the upgrade flow displays a button. Defaults to"Continue".limitLink(string, optional): The URL to open when the limit flow is configured to link users to a custom destination. Defaults to"".accessGroupIds(string[], optional): Member Access Group IDs that users can upgrade into from this group. Every ID must belong to the same studio and must reference a member group. Do not set this for invite_only groups. Defaults to[].isCTAButton(boolean, optional): Whether the limit experience should render a CTA button using limitCTA. Defaults totrue.isBuyMoreUses(boolean, optional): Whether users should be allowed to buy additional usage after reaching the limit. Defaults tofalse.isUpgradeToAnotherGroup(boolean, optional): Whether the limit experience should offer upgrades into other member access groups. This cannot be enabled for invite_only groups. Defaults tofalse.isLinkToCustomURL(boolean, optional): Whether the CTA should send the user to limitLink instead of an internal upgrade flow. Defaults tofalse.
currency(string | null, optional): Currency code used for paid groups, such as usd. This is ignored for free groups. Defaults tonull.ctaButtonText(string, optional): Text shown on the access-group card CTA in upgrade flows. Limited to 100 characters. Defaults to"Get".isDisplayImage(boolean, optional): Whether the access-group card should render its image in upgrade flows. Defaults tofalse.isArchived(boolean, optional): Whether the group should be marked as archived without deleting it. Defaults tofalse.derivedFromProductIds(string[], optional): Advanced/internal linkage to upstream product IDs that produced this access group. Defaults to[].
Invite-only Restrictions
An invite_only access group must be free and cannot set trialPeriodDays, upgrade.accessGroupIds, or upgrade.isUpgradeToAnotherGroup.
Examples
curl -X POST https://api.pickaxe.co/v1/studio/access-group/create \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"data": {
"type": "members",
"name": "Pro Members",
"description": "Access to premium tools",
"displayName": "Pro",
"displayDescription": "Unlock our premium tools and higher limits.",
"limit": 500,
"limitInterval": "month",
"ctaButtonText": "Upgrade",
"isDisplayImage": true,
"upgrade": {
"limitMessage": "Upgrade to keep going",
"limitCTA": "See plans",
"accessGroupIds": ["access-2MsL0vQeR8Ty"],
"isCTAButton": true,
"isBuyMoreUses": true,
"isUpgradeToAnotherGroup": true,
"isLinkToCustomURL": false
}
}
}'POST /studio/access-group/assign
Create associations between access groups and deployments or portals, either from the resource side or from the access-group side.
- Full URL:
https://api.pickaxe.co/v1/studio/access-group/assign
Request Fields
data(object, required): Assignment payload. Provide exactly one scope: accessGroupId, deploymentId, or portalId.mode("append" | "replace", optional): append adds missing links while preserving existing links and the current portal primary. replace makes the provided set the exact final state for the chosen scope and may change portal primaries. Defaults to"append".accessGroupId(string, optional): Use this when assigning deployments and/or portals to one access group. With portalIds and replace, this group becomes primary on every requested portal.deploymentId(string, optional): Use this when assigning one or more access groups to a single deployment.portalId(string, optional): Use this when assigning one or more access groups to a single portal.accessGroupIds(string[], optional): Required when deploymentId or portalId is provided. For portalId with replace, array order becomes association order and the first ID becomes primary. With append, the IDs are added after existing links and the current primary is preserved.deploymentIds(string[], optional): Required when accessGroupId is provided and you want to connect deployments to that access group.portalIds(string[], optional): Required when accessGroupId is provided and you want to connect portals to that access group. With replace, accessGroupId becomes primary on each requested portal; with append, it is only an additional connection.replacementAccessGroupId(string, optional): Used only with accessGroupId, portalIds, and mode=replace when the operation removes accessGroupId as another portal's primary. The replacement must be different, have the same type, be active, and satisfy the portal-link restrictions.
Portal Assignment Rules
- Every portal must always have exactly one non-null primary access group linked to it.
- For
portalId+accessGroupIds+replace, the first accessGroupIds entry becomes primary and the remaining entries are additional groups. Associationorderfollows the array order. - For
portalId+accessGroupIds+append, the existing primary is preserved and new groups are appended as additional groups. - For
accessGroupId+portalIds+replace, accessGroupId becomes primary on every requested portal. If it was primary on a portal being removed, replacementAccessGroupId is required so that portal never loses its primary. - For
accessGroupId+portalIds+append, the group is added as an additional connection and does not replace the current primary. - When the primary group is
publicormembers, additional groups must bemembersorinvite_only. - When the primary group is
invite_only, every additional group must also beinvite_only. publicandmembersaccess groups can each be assigned to only one portal. Aninvite_onlygroup can be assigned to multiple portals on any workspace plan.- Portal-link mutations reindex the remaining association order after assignment or removal.
- Assignments that violate these rules are rejected.
Access-group type is immutable after creation. PATCH requests that include type are rejected.
Examples
curl -X POST https://api.pickaxe.co/v1/studio/access-group/assign \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"data": {
"mode": "append",
"deploymentId": "deploy-1NsG6mQxT4Pv",
"accessGroupIds": [
"access-7VjA3sX2pQ9L",
"access-2MsL0vQeR8Ty"
]
}
}'POST /studio/access-group/remove
Remove associations between access groups and deployments or portals using the same scope rules as the assign endpoint.
- Full URL:
https://api.pickaxe.co/v1/studio/access-group/remove
Request Fields
data(object, required): Removal payload. Provide exactly one scope: accessGroupId, deploymentId, or portalId.accessGroupId(string, optional): Use this when removing deployments and/or portals from one access group.deploymentId(string, optional): Use this when removing one or more access groups from a single deployment.portalId(string, optional): Use this when removing one or more access groups from a single portal.accessGroupIds(string[], optional): Required when deploymentId or portalId is provided. These are the groups to disconnect from that resource.deploymentIds(string[], optional): Required when accessGroupId is provided and you want to disconnect deployments from that access group.portalIds(string[], optional): Required when accessGroupId is provided and you want to disconnect portals from that access group.
Portal Removal Rules
- A removal cannot disconnect a portal's primary access group. Promote or atomically replace the primary first.
- Successful portal removals reindex the remaining association
ordervalues.
Examples
curl -X POST https://api.pickaxe.co/v1/studio/access-group/remove \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"data": {
"portalId": "portal-4LsP0kNaW2Yd",
"accessGroupIds": ["access-7VjA3sX2pQ9L"]
}
}'GET /studio/access-group/<access_group_id>
Fetch one access group with computed counts and related deployment, portal, and parent-upgrade references.
- Full URL:
https://api.pickaxe.co/v1/studio/access-group/<access_group_id>
Request Fields
accessGroupId(string, required): The access-group ID to retrieve.
Examples
curl -X GET https://api.pickaxe.co/v1/studio/access-group/access-7VjA3sX2pQ9L \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN"PATCH /studio/access-group/<access_group_id>
Partially update an access group. Only the fields you send are changed.
The access-group type cannot be changed after creation and is not an accepted PATCH field.
An existing invite_only group cannot be updated with trialPeriodDays, upgrade.accessGroupIds, or upgrade.isUpgradeToAnotherGroup.
- Full URL:
https://api.pickaxe.co/v1/studio/access-group/<access_group_id>
Request Fields
accessGroupId(string, required): The access-group ID to update.data(object, required): Partial access-group payload. At least one real change is required.name(string, optional): Internal group name used in the workspace UI and administrative flows. Must be 1 to 60 characters when supplied. Defaults to"My New Access Group".description(string, optional): Internal description for the access group. Limited to 100 characters. Defaults to"".image(string | null, optional): Optional image URL used when the group card displays an image. Defaults tonull.displayName(string | null, optional): Optional public-facing card title. If omitted, clients typically fall back to name. Defaults tonull.displayDescription(string | null, optional): Optional public-facing card description. If omitted, clients typically fall back to description. Defaults tonull.limit(number, optional): Usage limit for the group. Use -1337 for unlimited access. Finite limits are typically paired with limitInterval. Defaults to-1337.limitInterval("day" | "week" | "month" | "year" | "one_time" | null, optional): The reset period for a finite limit. Use one_time for a non-recurring wallet or usage allocation. Defaults tonull.trialPeriodDays(number | null, optional): Positive number of trial days. This cannot be set forinvite_onlyaccess groups. Defaults tonull.upgrade(object, optional): Controls the experience shown after a user reaches their usage limit. Defaults to{...default upgrade payload}.limitMessage(string, optional): The message shown when a user reaches the access limit for this group. Defaults to"Please upgrade to continue".limitCTA(string, optional): The CTA label used when the upgrade flow displays a button. Defaults to"Continue".limitLink(string, optional): The URL to open when the limit flow is configured to link users to a custom destination. Defaults to"".accessGroupIds(string[], optional): Member Access Group IDs that users can upgrade into from this group. Every ID must belong to the same studio and must reference a member group. Do not set this for invite_only groups. Defaults to[].isCTAButton(boolean, optional): Whether the limit experience should render a CTA button using limitCTA. Defaults totrue.isBuyMoreUses(boolean, optional): Whether users should be allowed to buy additional usage after reaching the limit. Defaults tofalse.isUpgradeToAnotherGroup(boolean, optional): Whether the limit experience should offer upgrades into other member access groups. This cannot be enabled for invite_only groups. Defaults tofalse.isLinkToCustomURL(boolean, optional): Whether the CTA should send the user to limitLink instead of an internal upgrade flow. Defaults tofalse.
currency(string | null, optional): Currency code used for paid groups, such as usd. This is ignored for free groups. Defaults tonull.ctaButtonText(string, optional): Text shown on the access-group card CTA in upgrade flows. Limited to 100 characters. Defaults to"Get".isDisplayImage(boolean, optional): Whether the access-group card should render its image in upgrade flows. Defaults tofalse.isArchived(boolean, optional): Whether the group should be marked as archived without deleting it. Defaults tofalse.derivedFromProductIds(string[], optional): Advanced/internal linkage to upstream product IDs that produced this access group. Defaults to[].
Examples
curl -X PATCH https://api.pickaxe.co/v1/studio/access-group/access-7VjA3sX2pQ9L \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"data": {
"displayName": "Pro Plus",
"displayDescription": "Unlock premium tools and concierge support.",
"limit": 750,
"upgrade": {
"limitLink": "https://example.com/upgrade",
"isLinkToCustomURL": true
}
}
}'POST /studio/access-group/<access_group_id>/delete-preview
Preview how deleting an access group will affect users, subscriptions, deployment links, portal links, and parent upgrade paths.
- Full URL:
https://api.pickaxe.co/v1/studio/access-group/<access_group_id>/delete-preview
Request Fields
accessGroupId(string, required): The access-group ID you may delete.data(object, optional): Deletion mode configuration. Omit the body or send an empty object to preview a delete-only flow.mode("delete-only" | "reassign", optional): delete-only removes the access group and its links. reassign moves links and users into another access group before deletion. Defaults to"delete-only".newAccessGroupId(string, optional): Required when mode is reassign. It must be a different access group in the same studio and cannot be a public group.
Examples
curl -X POST https://api.pickaxe.co/v1/studio/access-group/access-7VjA3sX2pQ9L/delete-preview \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"data": {
"mode": "reassign",
"newAccessGroupId": "access-2MsL0vQeR8Ty"
}
}'POST /studio/access-group/<access_group_id>/delete
Delete an access group, optionally reassigning users and links into another member access group first.
- Full URL:
https://api.pickaxe.co/v1/studio/access-group/<access_group_id>/delete
Request Fields
accessGroupId(string, required): The access-group ID to delete.data(object, optional): Deletion mode configuration. Omit the body or send an empty object to perform a delete-only flow.mode("delete-only" | "reassign", optional): delete-only removes the group and associated links. reassign migrates links and user assignments to newAccessGroupId before deleting the original group. Defaults to"delete-only".newAccessGroupId(string, optional): Required when mode is reassign. It must be a different member access group in the same studio.
Examples
curl -X POST https://api.pickaxe.co/v1/studio/access-group/access-7VjA3sX2pQ9L/delete \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"data": {
"mode": "reassign",
"newAccessGroupId": "access-2MsL0vQeR8Ty"
}
}'