Hi here I am again :slightly_smiling_face: I'm try...
# permit-saas
m
Hi here I am again 🙂 I'm trying to model one of our use case with nested groups (final structure will have 4 levels, but for the example lets do it with first two company,location)
company#member
→
location#member
The plan for that is use group api. Firstly I have created groups for each group workspace: sea.live @project_id = default @env_id = user-management ### Create company group instance
curl -X POST "<https://api.permit.io/v2/schema/{{project_id}}/{{env_id}}/groups>" \
-H "Authorization: Bearer {{API_SECRET_KEY}}" \
-H "Content-Type: application/json" \
--data-raw '{
"group_resource_type_key": "company",
"group_instance_key": "clarksons",
"group_tenant": "default"
}'
### Create location group instance
curl -X POST "<https://api.permit.io/v2/schema/{{project_id}}/{{env_id}}/groups>" \
-H "Authorization: Bearer {{API_SECRET_KEY}}" \
-H "Content-Type: application/json" \
--data-raw '{
"group_resource_type_key": "location",
"group_instance_key": "london",
"group_tenant": "default"
}'
Next logical thing is assign group to group, so I have used endpoint ### company → location
curl -X PUT "<https://api.permit.io/v2/schema/{{project_id}}/{{env_id}}/groups/company:clarksons/assign_group>" \
-H "Authorization: Bearer {{API_SECRET_KEY}}" \
-H "Content-Type: application/json" \
--data-raw '{"group_instance_key": "london"}'
response is even I changed to --data-raw '{"group_instance_key": "location:london"}' it is not able to find.
Copy code
HTTP/1.1 404 Not Found
Date: Fri, 24 Oct 2025 09:37:14 GMT
Content-Type: application/json
Content-Length: 676
Connection: close
server: uvicorn
x-content-type-options: nosniff
referrer-policy: no-referrer
strict-transport-security: max-age=31536000; includeSubDomains; preload
x-request-id: 14661ae9ed184d7bb84c4bba63701abe
access-control-expose-headers: X-Request-ID

{
  "id": "14661ae9ed184d7bb84c4bba63701abe",
  "title": "We could not find the requested object/s",
  "error_code": "NOT_FOUND",
  "message": "The requested data could not be found, we could not find 'resourceinstance' with the given filters: env_id='aced0086-602a-49a6-8fbe-0c5aebefeb3a', resource_instance_id='london', resource_id='6195f099-37e1-478e-a3c9-7fb16b1d1412'. Please try again with different filters.\nIf you are sure there is an object with the given filters, contact our support on Slack for further guidance.",
  "additional_info": {
    "filters": {
      "env_id": "aced0086-602a-49a6-8fbe-0c5aebefeb3a",
      "resource_instance_id": "london",
      "resource_id": "6195f099-37e1-478e-a3c9-7fb16b1d1412"
    }
  }
}
Group is there and resource instances I can see on the UI: ###
curl -X GET "<https://api.permit.io/v2/schema/{{project_id}}/{{env_id}}/groups>" \
-H "Authorization: Bearer {{API_SECRET_KEY}}"
Copy code
HTTP/1.1 200 OK
Date: Fri, 24 Oct 2025 09:38:41 GMT
Content-Type: application/json
Content-Length: 603
Connection: close
server: uvicorn
x-content-type-options: nosniff
referrer-policy: no-referrer
strict-transport-security: max-age=31536000; includeSubDomains; preload
x-request-id: 55fd8d5a29df460b912a1a0de8690393
access-control-expose-headers: X-Request-ID

[
  {
    "assigned_roles": [
      "location:london#member"
    ],
    "users": [],
    "group_resource_type_key": "location",
    "group_instance_key": "london",
    "group_tenant": "default"
  },
  {
    "assigned_roles": [
      "company:clarksons#member"
    ],
    "users": [],
    "group_resource_type_key": "company",
    "group_instance_key": "clarksons",
    "group_tenant": "default"
  }
]
@Ask Inkeep what I missed here ?
a
Thanks for tagging me @Michał Wójcik. I'll look through relevant sources and get back to you shortly.
Looking at your error, the issue is with the endpoint URL format for assigning a group to another group. Based on the Groups API documentation, when assigning one group instance to another, you need to use the correct endpoint path. (1) The correct format for the assign_group endpoint should be:
Copy code
curl -X PUT \
'<https://api.permit.io/v2/schema/{project_id}/{env_id}/groups/{group_instance_key}/assign_group>' \
-H 'authorization: Bearer API_SECRET_KEY' \
--data-raw '{"group_instance_key": "org1"}'
In your case, you're using
company:clarksons
in the URL path, but based on the example, it appears you should use just the group instance key without the resource type prefix in the URL. The documentation shows using just the instance key in the path. Try this instead:
Copy code
curl -X PUT "<https://api.permit.io/v2/schema/{{project_id}}/{{env_id}}/groups/clarksons/assign_group>" \
  -H "Authorization: Bearer {{API_SECRET_KEY}}" \
  -H "Content-Type: application/json" \
  --data-raw '{"group_instance_key": "london"}'
The error message indicates it's looking for a resource instance rather than recognizing this as a group-to-group assignment, whi
To learn more, see the sources I considered: [[1] Groups API](https://docs.permit.io/api/rebac/groups)
m
Unfortunately @Ask Inkeep
curl -X PUT "<https://api.permit.io/v2/schema/{{project_id}}/{{env_id}}/groups/clarksons/assign_group>" \
-H "Authorization: Bearer {{API_SECRET_KEY}}" \
-H "Content-Type: application/json" \
--data-raw '{"group_instance_key": "london"}'
Copy code
HTTP/1.1 404 Not Found
Date: Fri, 24 Oct 2025 09:59:33 GMT
Content-Type: application/json
Content-Length: 543
Connection: close
server: uvicorn
x-content-type-options: nosniff
referrer-policy: no-referrer
strict-transport-security: max-age=31536000; includeSubDomains; preload
x-request-id: 9920033ef97a42cfa409a2df73df70d9
access-control-expose-headers: X-Request-ID

{
  "id": "9920033ef97a42cfa409a2df73df70d9",
  "title": "We could not find the requested object/s",
  "error_code": "NOT_FOUND",
  "message": "The requested data could not be found, we could not find 'resource' with the given filters: env_id='aced0086-602a-49a6-8fbe-0c5aebefeb3a', resource_id='group'. Please try again with different filters.\nIf you are sure there is an object with the given filters, contact our support on Slack for further guidance.",
  "additional_info": {
    "filters": {
      "env_id": "aced0086-602a-49a6-8fbe-0c5aebefeb3a",
      "resource_id": "group"
    }
  }
}
a
Thanks for tagging me @Michał Wójcik. I'll look through relevant sources and get back to you shortly.
Looking at the error message, it seems the API is looking for a resource called "group" which doesn't exist. The issue appears to be that the Groups API documentation shows creating groups as resource instances, but the assign_group endpoint might not be fully documented in the available sources. (1) Based on the Groups API documentation, when you add a group to another group, the example shows adding a group instance
org1
to another group:
Copy code
curl '<https://api.permit.io/v2/schema/{project_id}/{env_id}/groups>' \  
  

...
However, the specific endpoint format for
assign_group
that you're using isn't clearly documented in the available sources. The error suggests the system is interpreting "groups" in your URL path as a resource type that doesn't exist. Since you're experiencing this issue with the Groups API endpoints, I'd recommend: ​*To help our engineers investigate:*​ 1. Which Workspace / Project / Env are you using? (You mentioned env_id='aced0086-602a-49a6-8fbe-0c5aebefeb3a') 2. What exact SDK version or API calls are you performing? (You've provided the CURL commands) 3. Which PDP version are you running? 4. Could you share any relevant PDP logs if you have them? The Groups API documentation shows examples of creating groups and assigning users to groups, but the specific group-to-group assignment endpoint syntax might need clarification from our engineering team.
To learn more, see the sources I considered: [[1] Groups API](https://docs.permit.io/api/rebac/groups)
Thanks for tagging me @Michał Wójcik. I'll look through relevant sources and get back to you shortly.
Looking at your issue with assigning a group to another group, the problem is that you're trying to use an endpoint that doesn't exist in the Groups API. Based on the documentation (1) , to add a group to another group, you need to use a different approach. Instead of trying to assign one group directly to another, you should assign a role to the parent group that references the child group's resource instance. Here's the correct way to create the nested group relationship:
Copy code
curl -X POST '<https://api.permit.io/v2/schema/{project_id}/{env_id}/group/company:clarksons/roles>' \
 -H 'authorization: Bearer API_SECRET_KEY' \
 --data-raw '{"resource": "location", "resource_instance": "london", "role": "member", "tenant":"default"}'
This creates a role assignment where the
company:clarksons
group gets the
member
role on the
location:london
resource instance . The Groups API will then automatically create the necessary derivations between the groups. The key difference is: • You're not assigning a group to a group directly • You're assigning a role to the parent group that references the child group as a resource instance • The
group_instance_key
format in role assignments should be
type:instance
(e.g.,
company:clarksons
) The automated role derivation will then ensure that members of the
company#member
role will automatically inherit the
location#member
role for the london location . To help our engineers investigate further: 1. Which Workspace / Project / Env are you using (you mentioned sea.live/default/user-management) 2. What SDK calls are you making (you've shown the CURL commands) 3. Which PDP version are you running 4. Could you share relevant logs from your PDP
To learn more, see the sources I considered: [[1] Groups API](https://docs.permit.io/api/rebac/groups)
Appreciate the feedback! Feel free to ask me again with additional guidance or context.
m
Could I ask you permit team assist me, please? AI did not help this time :(
a
Hey @Michał Wójcik, sure! Will circle back to you soon!
o
I’m on mobile but quick observation The CURL you posted has project-id and env-id templated instead of the actual values as listed in the top of this thread
m
Thanks for the responses. The values have been replaced in the curls, but the response is still 404.
t
Hey Michael, give me a while to test this out locally and get back
Figured it out. it was the wrong api call. I created a shell script that is doing everything correctly:
I left my test env in there so feel free to run it as is.
✅ 2
(however, you won't see it in your UI so you might as well change it, but it's at least proof it works 😅 )
m
@Thomas
"Figured it out. it was the *wrong api call.* I created a shell script that is doing everything correctly:"
Could you explain what was wrong with the assign_group endpoint? I see that you have replaced it with the creation of a relationship between resource types. Is it exactly the same? How is progress on the groups UI going? Could I become a beta tester? :)
a
Hey @Michał Wójcik, will make sure @Thomas see it and will circle back to you soon!
s
Hi @Michał Wójcik I’m looking into this for you. This should work correctly if you’re using the default resource type “group” for both of your groups. We’re actively working on improvements in this area, including a better interface and enhanced API functionality. This is currently a high priority for our team. I’ll keep you updated on our progress!
✅ 1
m
Guys, I just wanted to give you an update. I misinterpreted the assign_group endpoint. It works in such a way that I can assign a group within the same 'resource type-group type' instance to instance. I tried to do it as an assignment to different group types. So Thomas response with creation relationship between resource types was correct:) Having you here i have another question. Is there any possibility to subscribe on my side on event or webhook, that someone change something on user, policy, permission etc.? @Ask Inkeep could you help
a
Thanks for tagging me @Michał Wójcik. I'll look through relevant sources and get back to you shortly.
Yes, you can subscribe to events through webhooks when changes occur in Permit.io. The webhook functionality is available for Permit Elements and provides notifications for various user and permission-related events. (1) Setting Up Webhooks You can configure webhooks to receive notifications for actions performed by end users, such as creating new users, approving/denying access requests, and role assignments. To set up a webhook: 1. ​*Create your endpoint*​ - Set up a URL endpoint on your server that can receive POST requests 2. ​*Configure the webhook*​ in Permit.io with your endpoint URL 3. ​*Secure your endpoint*​ using a secret for validation and HTTPS 4. ​*Respond with HTTP 200*​ to acknowledge receipt Available Webhook Events The documented webhook schemas include: ​*User Management Events:*​ • ​*Create User*​ - Triggered when a new user is created
Copy code
{  
 email: str,  
 tenant_key: str,  
 role: str,  
 permission_level: str,  
 type: ElementsWebhookType = "create_user"  
}
• ​*Invite User*​ - Triggered when a user is invited • ​*Role Assignment*​ - Triggered when roles are assigned to users
Copy code
{  
 user_id: str,  
 tenant_id: str,  
 role: str,  
 permission_level: str,  
 type: ElementsWebhookType = "role_assignment"  
}
​*Access Control Events:*​ • ​*Access Request*​ - Notifications for access request status changes • ​*Operation Approval*​ - Notifications for operation approval workflows The Ruby SDK also includes webhook models with properties for webhook configuration including URL and bearer token authentication. (2) (3) For real-time policy and data updates, you might also consider OPAL which supports webhook integration for Git repository changes (4) and can be configured to receive webhooks from services like GitHub, GitLab, Azure DevOps, and Bitbucket.
o
@Ask Inkeep got a bit confused here - this is the right webhook/callback https://docs.permit.io/concepts/pdp/configuration/#pdp_data_update_callbacks
a
Thanks for tagging me @Or Weis. I'll look through relevant sources and get back to you shortly.
You're correct! For notifications about policy and permission changes, you should use the PDP data update callbacks feature. (1) The
PDP_DATA_UPDATE_CALLBACKS
configuration allows you to receive webhooks when data updates occur in your PDP. This is triggered after successful data synchronization events. Additionally, OPAL (which powers the PDP) provides a data update callback feature that can be activated by setting: (2)
Copy code
OPAL_SHOULD_REPORT_ON_DATA_UPDATES=True
This will trigger HTTP callbacks to a configurable URL after every successful data update, allowing you to track which updates completed successfully and were correctly saved to the OPA cache. For PDP sync errors specifically, you can configure webhooks to be notified when data synchronization fails: (3)
Copy code
curl \
  -H 'Authorization: Bearer API_SECRET_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"type": "pdp_sync_error", "url": "<https://your.webhook.receiver/path/to/webhook>"}' 
  '<https://api.permit.io/v2/projects/{project_id}/envs/{env_id}/webooks>'
The webhook payload for sync errors will include:
Copy code
{
  "org_id": "a40c5d1f-d889-43e9-94ea-b9b33585fc6b",
  "project_id": "405d8339-3514-403b-8c43-83ae74cfe0e9",
  "env_id": "3f967d0d-4d55-42f3-97c4-6a5455dfe4a6",
  "pdp_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"],
}
To learn more, see the sources I considered: [[1] Configurations](https://docs.permit.io/concepts/pdp/configuration) [[2] How to use data update callbacks and OPA healthcheck policy](https://docs.opal.ac/tutorials/healthcheck_policy_and_update_callbacks) [[3] PDP Sync Error Webhooks](https://docs.permit.io/api/pdp-webhooks)
t
Hey @Michał Wójcik good to hear. The main way to get events is to poll the API for all the events and audit logs on the system into a SIEM or log aggregator and filter what you need from there. If it's something where you need an inline hook once data or policy changes, what @Or Weis sent will work too but it comes at a performance overhead. In the scenario that you aren't using it for part of policy eval, it may be good to spin up a second PDP that just does the webhook and isn't used for any check() calls.
TL;DR if it's not critical inline, polling the logs is the best action but you also have pdp_data_update_callbacks
m
The reason I am asking you this is that one team works according to CQRS, they have a relational database for writing and NoSQL for reading, where they build read models, the key is userId and value resources to which the user has access. Hence their question about the possibility of subscribing and updating the read model status. I can see that this is roughly possible with permit.io. I see only two drawbacks here: authorization is not fully centralized then... and when displaying, they do not query the Check endpoint, because current state is in their database (updated through events), which means they do not have an audit log. What is your opinion or advice in this case? @Thomas
🙌 1
a
Hey @Michał Wójcik, Looping in the team so we can give you a better answer, meanwhile i'll let @Ask Inkeep give an advice.
a
Thanks for tagging me @Alon Boshi. I'll look through relevant sources and get back to you shortly.
For your CQRS architecture with separate read/write databases, there are important considerations when integrating with Permit.io: ​*Audit Log Implications*​ You're correct that bypassing the Check endpoint means losing audit logs. Permit.io automatically generates audit logs from access checks (1) , and these logs are crucial for compliance and debugging. The best practice is automatic generation from access checks to ensure full coverage . ​*Event-Driven Architecture Considerations*​ In event-driven architectures like CQRS, authorization becomes more complex because of the decoupled nature

(2) ▾

. The challenge is that "with event driven architecture, things are so decoupled, where you typically might have, I make an API call, then I have the permissions to do certain things inside that API. You kind of lose that in event driven architecture" . ​*Hybrid Approach Recommendation*​ Consider a hybrid approach: 1. Use Permit.io as your centralized authorization source of truth 2. Cache authorization decisions in your read model for performance 3. Periodically validate and refresh cached permissions 4. For critical operations, always call the Check endpoint directly ​*Real-time Synchronization Options*​ Based on the discussion about OPAL and Kafka integration (3) , Permit.io's infrastructure can handle real-time data streaming. However, the GitHub discussion mentions bottlenecks when "OPAL server fetching events from kafka and sending to client. Slow send to clients causes having them all in memory and eventually OOM" . For your CQRS scenario, I'd recommend treating Permit.io as the authoritative source while using your read model as a performance optimization layer, not a replacement for centralized authorization. This maintains audit trails for compliance while supporting your architectural patterns.
To learn more, see the sources I considered: [[1] Best Practices for Authorization Audit Logs](https://www.permit.io/blog/audit-logs) [[2] The Security Challenges of Event Driven Design](

https://www.youtube.com/watch?v=zcKGZMaqD9U▾

) [[3] How to properly scale out OPAL servers for data updates directly from Kafka](https://github.com/permitio/opal/discussions/671)