Organizations & Projects
Learn about the organizational structure in perms.io and how to work with your existing setup.
Overview
Your perms.io setup is built around organizations as the top-level container for all resources. Within an organization, you can have projects to logically separate different applications or environments.
Key Concepts
- Organization: The primary container for all resources
- Project: A logical grouping within an organization (e.g., "production", "staging")
- User: Members of an organization with various roles and permissions
- API Key: Programmatic access credentials scoped to a specific project
Authentication
All API endpoints require authentication via:
- Bearer Token: For user-based authentication
- API Key: For programmatic access (format: Bearer pk_<key>)
Base URL
https://api.perms.io/organisation-service/v1
Organization Management
Organizations are created and managed through the dashboard. Once created, you can work with your organization's data through the API.
Get Organization
Retrieves details of a specific organisation.
curl -X GET "https://api.perms.io/organisation-service/v1/organisation/{id}" \
-H "Authorization: Bearer YOUR_TOKEN"
func getOrganisation(client organisationv1.OrganisationServiceClient, orgId string) {
req := &organisationv1.GetOrganisationRequest{
Id: orgId,
}
resp, err := client.GetOrganisation(context.Background(), req)
if err != nil {
log.Fatalf("Failed to get organisation: %v", err)
}
log.Printf("Organisation: %s (%s)", resp.Organisation.Name, resp.Organisation.Id)
}
async fn get_organisation(client: &mut OrganisationServiceClient<Channel>, org_id: &str) -> Result<(), Box<dyn std::error::Error>> {
let request = tonic::Request::new(GetOrganisationRequest {
id: org_id.to_string(),
});
let response = client.get_organisation(request).await?;
let org = response.get_ref().organisation.as_ref().unwrap();
println!("Organisation: {} ({})", org.name, org.id);
Ok(())
}
def get_organisation(org_id: str):
channel = grpc.secure_channel('api.perms.io:443', grpc.ssl_channel_credentials())
client = organisation_service_pb2_grpc.OrganisationServiceStub(channel)
request = organisation_service_pb2.GetOrganisationRequest(id=org_id)
response = client.GetOrganisation(request)
print(f"Organisation: {response.organisation.name} ({response.organisation.id})")
public void getOrganisation(String orgId) {
ManagedChannel channel = ManagedChannelBuilder.forAddress("api.perms.io", 443)
.useTransportSecurity()
.build();
OrganisationServiceGrpc.OrganisationServiceBlockingStub client =
OrganisationServiceGrpc.newBlockingStub(channel);
GetOrganisationRequest request = GetOrganisationRequest.newBuilder()
.setId(orgId)
.build();
GetOrganisationResponse response = client.getOrganisation(request);
System.out.println("Organisation: " + response.getOrganisation().getName() +
" (" + response.getOrganisation().getId() + ")");
channel.shutdown();
}
interface GetOrganisationRequest {
id: string;
}
function getOrganisation(orgId: string): void {
const request: GetOrganisationRequest = {
id: orgId
};
client.GetOrganisation(request, (error: grpc.ServiceError | null, response: any) => {
if (error) {
console.error('Error:', error);
return;
}
console.log(`Organisation: ${response.organisation.name} (${response.organisation.id})`);
});
}
Project Management
Projects are created and managed through the dashboard. Use the API to work with your existing projects.
Get Project
Retrieves details of a specific project. Requires project.get permission.
curl -X GET "https://api.perms.io/organisation-service/v1/organisation/{organisation_id}/project?project_name={project_name}" \
-H "Authorization: Bearer YOUR_TOKEN"
List Projects
Lists all projects within an organisation. Requires project.get permission.
curl -X GET "https://api.perms.io/organisation-service/v1/organisation/{organisation_id}/projects?limit=10&cursor=abc123" \
-H "Authorization: Bearer YOUR_TOKEN"
func listProjects(client organisationv1.OrganisationServiceClient, orgId string) {
req := &organisationv1.GetProjectsForOrganisationRequest{
OrganisationId: orgId,
Pagination: &paginationv1.PaginationRequest{
Limit: proto.Int32(10),
},
}
resp, err := client.GetProjectsForOrganisation(context.Background(), req)
if err != nil {
log.Fatalf("Failed to list projects: %v", err)
}
for _, project := range resp.Projects {
log.Printf("Project: %s", project.Name)
}
}
use permio_proto::pagination::PaginationRequest;
async fn list_projects(client: &mut OrganisationServiceClient<Channel>, org_id: &str) -> Result<(), Box<dyn std::error::Error>> {
let request = tonic::Request::new(GetProjectsForOrganisationRequest {
organisation_id: org_id.to_string(),
pagination: Some(PaginationRequest {
limit: Some(10),
cursor: None,
}),
search: None,
});
let response = client.get_projects_for_organisation(request).await?;
for project in response.get_ref().projects.iter() {
println!("Project: {}", project.name);
}
Ok(())
}
def list_projects(org_id: str):
channel = grpc.secure_channel('api.perms.io:443', grpc.ssl_channel_credentials())
client = organisation_service_pb2_grpc.OrganisationServiceStub(channel)
request = organisation_service_pb2.GetProjectsForOrganisationRequest(
organisation_id=org_id,
pagination=pagination_pb2.PaginationRequest(limit=10)
)
response = client.GetProjectsForOrganisation(request)
for project in response.projects:
print(f"Project: {project.name}")
public void listProjects(String orgId) {
ManagedChannel channel = ManagedChannelBuilder.forAddress("api.perms.io", 443)
.useTransportSecurity()
.build();
OrganisationServiceGrpc.OrganisationServiceBlockingStub client =
OrganisationServiceGrpc.newBlockingStub(channel);
GetProjectsForOrganisationRequest request = GetProjectsForOrganisationRequest.newBuilder()
.setOrganisationId(orgId)
.setPagination(PaginationRequest.newBuilder().setLimit(10).build())
.build();
GetProjectsForOrganisationResponse response = client.getProjectsForOrganisation(request);
for (Project project : response.getProjectsList()) {
System.out.println("Project: " + project.getName());
}
channel.shutdown();
}
interface GetProjectsForOrganisationRequest {
organisation_id: string;
pagination?: {
limit?: number;
cursor?: string;
};
search?: string;
}
function listProjects(orgId: string): void {
const request: GetProjectsForOrganisationRequest = {
organisation_id: orgId,
pagination: {
limit: 10
}
};
client.GetProjectsForOrganisation(request, (error: grpc.ServiceError | null, response: any) => {
if (error) {
console.error('Error:', error);
return;
}
response.projects.forEach((project: any) => {
console.log(`Project: ${project.name}`);
});
});
}
API Key Management
API keys are created and managed through the dashboard. Use the API to list your existing keys.
List API Keys
Lists all API keys for a project. Requires apiKeys.list permission.
curl -X GET "https://api.perms.io/organisation-service/v1/organisation/{organisation_id}/api-key?project_name={project_name}" \
-H "Authorization: Bearer YOUR_TOKEN"
User Management
Invite User to Organisation
Invites a user to join an organisation via email.
curl -X POST "https://api.perms.io/organisation-service/v1/organisation/{organisation_id}/invite" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com"
}'
func inviteUser(client organisationv1.OrganisationServiceClient, orgId, email string) {
req := &organisationv1.InviteUserToOrganisationRequest{
OrganisationId: orgId,
Email: email,
}
_, err := client.InviteUserToOrganisation(context.Background(), req)
if err != nil {
log.Fatalf("Failed to invite user: %v", err)
}
log.Printf("Invited user: %s", email)
}
async fn invite_user(client: &mut OrganisationServiceClient<Channel>, org_id: &str, email: &str) -> Result<(), Box<dyn std::error::Error>> {
let request = tonic::Request::new(InviteUserToOrganisationRequest {
organisation_id: org_id.to_string(),
email: email.to_string(),
});
client.invite_user_to_organisation(request).await?;
println!("Invited user: {}", email);
Ok(())
}
def invite_user(org_id: str, email: str):
channel = grpc.secure_channel('api.perms.io:443', grpc.ssl_channel_credentials())
client = organisation_service_pb2_grpc.OrganisationServiceStub(channel)
request = organisation_service_pb2.InviteUserToOrganisationRequest(
organisation_id=org_id,
email=email
)
client.InviteUserToOrganisation(request)
print(f"Invited user: {email}")
public void inviteUser(String orgId, String email) {
ManagedChannel channel = ManagedChannelBuilder.forAddress("api.perms.io", 443)
.useTransportSecurity()
.build();
OrganisationServiceGrpc.OrganisationServiceBlockingStub client =
OrganisationServiceGrpc.newBlockingStub(channel);
InviteUserToOrganisationRequest request = InviteUserToOrganisationRequest.newBuilder()
.setOrganisationId(orgId)
.setEmail(email)
.build();
client.inviteUserToOrganisation(request);
System.out.println("Invited user: " + email);
channel.shutdown();
}
interface InviteUserToOrganisationRequest {
organisation_id: string;
email: string;
}
function inviteUser(orgId: string, email: string): void {
const request: InviteUserToOrganisationRequest = {
organisation_id: orgId,
email: email
};
client.InviteUserToOrganisation(request, (error: grpc.ServiceError | null, response: any) => {
if (error) {
console.error('Error:', error);
return;
}
console.log(`Invited user: ${email}`);
});
}
Remove User from Organisation
Removes a user from an organisation. Requires organisation.user.remove permission.
curl -X POST "https://api.perms.io/organisation-service/v1/organisation/{organisation_id}/user/{user_id}" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'
Get User's Organisation
Retrieves the organisation associated with a user. Requires user.organisation.get permission.
curl -X GET "https://api.perms.io/organisation-service/v1/organisation/user" \
-H "Authorization: Bearer YOUR_TOKEN"
Dashboard & Analytics
Get Dashboard
Retrieves dashboard statistics for an organisation. Requires organisation.dashboard.view permission.
curl -X GET "https://api.perms.io/organisation-service/v1/organisation/{organisation_id}/dashboard" \
-H "Authorization: Bearer YOUR_TOKEN"
func getDashboard(client organisationv1.OrganisationServiceClient, orgId string) {
req := &organisationv1.GetOrganisationDashboardRequest{
OrganisationId: orgId,
}
resp, err := client.GetOrganisationDashboard(context.Background(), req)
if err != nil {
log.Fatalf("Failed to get dashboard: %v", err)
}
log.Printf("Dashboard stats - Roles: %d, Permissions: %d, Principals: %d, Users: %d",
resp.TotalRoles, resp.TotalPermissions, resp.TotalPrincipals, resp.TotalUsers)
}
async fn get_dashboard(client: &mut OrganisationServiceClient<Channel>, org_id: &str) -> Result<(), Box<dyn std::error::Error>> {
let request = tonic::Request::new(GetOrganisationDashboardRequest {
organisation_id: org_id.to_string(),
});
let response = client.get_organisation_dashboard(request).await?;
let dashboard = response.get_ref();
println!("Dashboard stats - Roles: {}, Permissions: {}, Principals: {}, Users: {}",
dashboard.total_roles, dashboard.total_permissions, dashboard.total_principals, dashboard.total_users);
Ok(())
}
def get_dashboard(org_id: str):
channel = grpc.secure_channel('api.perms.io:443', grpc.ssl_channel_credentials())
client = organisation_service_pb2_grpc.OrganisationServiceStub(channel)
request = organisation_service_pb2.GetOrganisationDashboardRequest(
organisation_id=org_id
)
response = client.GetOrganisationDashboard(request)
print(f"Dashboard stats - Roles: {response.total_roles}, Permissions: {response.total_permissions}, "
f"Principals: {response.total_principals}, Users: {response.total_users}")
public void getDashboard(String orgId) {
ManagedChannel channel = ManagedChannelBuilder.forAddress("api.perms.io", 443)
.useTransportSecurity()
.build();
OrganisationServiceGrpc.OrganisationServiceBlockingStub client =
OrganisationServiceGrpc.newBlockingStub(channel);
GetOrganisationDashboardRequest request = GetOrganisationDashboardRequest.newBuilder()
.setOrganisationId(orgId)
.build();
GetOrganisationDashboardResponse response = client.getOrganisationDashboard(request);
System.out.println(String.format("Dashboard stats - Roles: %d, Permissions: %d, Principals: %d, Users: %d",
response.getTotalRoles(), response.getTotalPermissions(),
response.getTotalPrincipals(), response.getTotalUsers()));
channel.shutdown();
}
interface GetOrganisationDashboardRequest {
organisation_id: string;
}
function getDashboard(orgId: string): void {
const request: GetOrganisationDashboardRequest = {
organisation_id: orgId
};
client.GetOrganisationDashboard(request, (error: grpc.ServiceError | null, response: any) => {
if (error) {
console.error('Error:', error);
return;
}
console.log(`Dashboard stats - Roles: ${response.total_roles}, Permissions: ${response.total_permissions}, ` +
`Principals: ${response.total_principals}, Users: ${response.total_users}`);
});
}
Get Principal Usage Stats
Retrieves usage statistics for billing purposes. Requires billing.view permission.
curl -X GET "https://api.perms.io/organisation-service/v1/organisation/{organisation_id}/principal-usage" \
-H "Authorization: Bearer YOUR_TOKEN"
Get Usage History
Retrieves historical usage data for charting. Requires billing.view permission.
curl -X GET "https://api.perms.io/organisation-service/v1/organisation/{organisation_id}/usage-history?days=30" \
-H "Authorization: Bearer YOUR_TOKEN"
Error Handling
The service returns standard gRPC status codes:
OK(0): SuccessINVALID_ARGUMENT(3): Invalid request parametersNOT_FOUND(5): Resource not foundPERMISSION_DENIED(7): Insufficient permissionsALREADY_EXISTS(6): Resource already existsRESOURCE_EXHAUSTED(8): Rate limit exceeded
Example Error Response
{
"error": {
"code": 7,
"message": "Permission denied: missing required permission 'project.create' on resource '/organisation/org_123'"
}
}
Rate Limits
API requests are subject to rate limiting: - Authenticated users: 1000 requests per hour - API keys: 10000 requests per hour - Burst limit: 100 requests per minute
Setup Instructions
To set up your organization structure:
- Create Organization: Use the dashboard to create your organization
- Add Projects: Create projects for different environments or applications
- Generate API Keys: Create API keys for programmatic access to specific projects
- Invite Users: Add team members to your organization
- Configure Permissions: Use the permissions API to set up your access control
For detailed setup instructions, see the Quick Start Guide.