Skip to main content
Permify exposes its authorization APIs over both REST (port 3476) and gRPC (port 3478). The Permission Service is the primary interface for enforcing access control at runtime.

API overview

The Permission Service provides the following endpoints:
EndpointPurpose
POST /v1/tenants/{tenant_id}/permissions/checkCheck if a subject can perform an action on an entity
POST /v1/tenants/{tenant_id}/permissions/bulk-checkCheck multiple permissions in a single request
POST /v1/tenants/{tenant_id}/permissions/lookup-entityFind all entities a subject can access
POST /v1/tenants/{tenant_id}/permissions/lookup-subjectFind all subjects that can access an entity
POST /v1/tenants/{tenant_id}/permissions/expandExpand an action into the full set of relations it resolves to

SDKs

Official SDK clients are available for:

Authentication

Permify supports OpenID Connect and Pre-Shared Keys for API authentication. Both methods are configurable via the configuration YAML file or CLI flags. Once enabled, pass a Bearer token with every request.

Check API

The Check API answers the question: Can user U perform action X on resource Y? POST /v1/tenants/{tenant_id}/permissions/check
curl --location --request POST 'localhost:3476/v1/tenants/t1/permissions/check' \
--header 'Content-Type: application/json' \
--data-raw '{
  "metadata": {
    "snap_token": "",
    "schema_version": "",
    "depth": 20
  },
  "entity": {
    "type": "repository",
    "id": "1"
  },
  "permission": "edit",
  "subject": {
    "type": "user",
    "id": "1",
    "relation": ""
  }
}'
Response:
{
  "can": "RESULT_ALLOW",
  "metadata": {
    "check_count": 0
  }
}
The can field returns one of:
  • RESULT_ALLOW — access is granted.
  • RESULT_DENY — access is denied.

How access decisions are evaluated

When Permify receives a check request, it:
  1. Looks up your authorization model (schema) to find the relations that compose the requested action.
  2. Walks the relationship graph to determine whether the subject is connected to the resource through those relations.
For example, given a document entity with action edit = parent.admin or owner, Permify runs two concurrent queries:
  • Q1: Is the subject an owner of the document?
  • Q2: Is the subject an admin of the document’s parent organization?
With an or relation, the engine returns RESULT_ALLOW as soon as either query resolves to true. With an and relation, both must resolve to true.
With the right infrastructure, Permify delivers 7–12 ms check latency, and up to 30 ms under heavy load. See Cache Mechanisms for tuning options.

Attribute-based check with context

You can pass runtime context data (e.g. IP address, time) alongside a check request for ABAC evaluation:
client.permission.check({
    tenantId: "t1",
    metadata: {
        snapToken: "",
        schemaVersion: "",
        depth: 20,
    },
    entity: {
        type: "organization",
        id: "1",
    },
    permission: "hr_manager",
    subject: {
        type: "user",
        id: "1",
    },
    context: {
        data: {
            ip_address: "192.158.1.38",
        },
    },
}).then((response) => {
    if (response.can === PermissionCheckResponse_Result.RESULT_ALLOWED) {
        console.log("RESULT_ALLOWED");
    } else {
        console.log("RESULT_DENIED");
    }
});

Bulk Check API

The Bulk Check API lets you verify multiple permissions in a single request, reducing network round trips when you need to render UI elements conditionally or check access for a list of resources. POST /v1/tenants/{tenant_id}/permissions/bulk-check
You can check up to 100 permissions in a single bulk-check call. All checks are evaluated against the same authorization data snapshot.
curl --location --request POST 'localhost:3476/v1/tenants/t1/permissions/bulk-check' \
--header 'Content-Type: application/json' \
--data-raw '{
  "metadata": {
    "snap_token": "",
    "schema_version": "",
    "depth": 20
  },
  "checks": [
    {
      "entity": { "type": "repository", "id": "1" },
      "permission": "edit",
      "subject": { "type": "user", "id": "1" }
    },
    {
      "entity": { "type": "repository", "id": "2" },
      "permission": "delete",
      "subject": { "type": "user", "id": "1" }
    }
  ]
}'
This is particularly useful for showing or hiding UI elements (buttons, menu items) based on multiple permissions at once.

Lookup Entity

Lookup Entity answers: Which resources can user X perform action Y on? POST /v1/tenants/{tenant_id}/permissions/lookup-entity Instead of querying all resources and checking each one, Permify first finds the relations linked to the requested action, then queries those relations for the given subject. This makes reverse lookups significantly more efficient.
curl --location --request POST 'localhost:3476/v1/tenants/t1/permissions/lookup-entity' \
--header 'Content-Type: application/json' \
--data-raw '{
  "metadata": {
    "snap_token": "",
    "schema_version": "",
    "depth": 20
  },
  "entity_type": "repository",
  "permission": "edit",
  "subject": {
    "type": "user",
    "id": "1"
  }
}'
Response:
{
  "entity_ids": ["1", "3", "5"]
}
The response contains the IDs of all repository entities that user:1 can edit.
For large datasets, use the Lookup Entity endpoint with pagination via continuous_token to retrieve results in pages.

Lookup Subject

Lookup Subject answers: Who can perform action Y on entity Z? POST /v1/tenants/{tenant_id}/permissions/lookup-subject
curl --location --request POST 'localhost:3476/v1/tenants/t1/permissions/lookup-subject' \
--header 'Content-Type: application/json' \
--data-raw '{
  "metadata": {
    "snap_token": "",
    "schema_version": "",
    "depth": 20
  },
  "entity": {
    "type": "repository",
    "id": "1"
  },
  "permission": "edit",
  "subject_reference": {
    "type": "user"
  }
}'
Response:
{
  "subject_ids": ["1", "3"]
}
The response contains the IDs of all user subjects that can edit repository:1.

Additional APIs

Permify provides a full suite of authorization APIs beyond permission checks:
APIDescription
Write SchemaConfigure your authorization model
Write DataWrite relational tuples and attributes
Read RelationshipsRead and filter stored relation tuples
Delete DataRemove relation tuples or attributes
Expand APIExpand an action into its full relation tree
Watch APIStream real-time changes to relation tuples

Performance

Permify targets 7–12 ms end-to-end check latency with the right architecture. Under heavy load with cache and database tuning you may see up to 30 ms. The default rate limit is 100 requests per second (configurable). See Cache Mechanisms for details on tuning.
Permify is compatible with service meshes such as Istio since it uses standard gRPC and HTTP protocols. You will need to configure your service mesh manually.

Build docs developers (and LLMs) love