Migrate from the v1 API to v2
Move an application from the deprecated Permit.io v1 API, v1 policy decision point (PDP), and v1 SDKs to v2. This page is for maintainers of code that calls the v1 API or runs a v1 PDP. The first half is the migration procedure, in the order to run it. The second half is a reference of the v2 object hierarchy, paths, and keys that the procedure uses.
Who needs to migrate
Migrate if your code does any of the following:
- Calls the Permit REST API at a v1 path.
- Runs a v1 PDP container.
- Uses a v1 SDK.
The v1 API and v1 PDPs are deprecated. Features added in v2, such as attribute-based access control (ABAC) condition sets, are available only in the v2 API.
What changed in v2
| Area | v1 | v2 |
|---|---|---|
| Object hierarchy | Objects could live at the organization level | Organizations contain projects, projects contain environments, and environments contain almost every other object. See v2 object hierarchy. |
| API roots | Mixed | General objects at /v2/orgs and /v2/projects, schema objects under /v2/schema, and facts objects under /v2/facts |
| Identifiers | Slugs, custom_id values, and keys in different places | Keys everywhere. Every path accepts either the key or the Permit ID of an object. See Keys. |
| Organization-scoped schema objects | Supported | Not supported. Move them into an environment. See Move off organization-scoped objects. |
| SDK write calls | v1 write API | A different write API. Code that writes through the SDK must change. See Upgrade the PDP and SDKs. |
permit.check() and SDK initialization | v1 calls | Same calls as v1 |
The v1 and v2 APIs read and write the same data, so you can use both during the migration. The two APIs make different assumptions about the structure of data, so a v1 call can behave differently from the v2 call for the same object.
Migration order
- Move off organization-scoped objects, if you have any. Do this first, because a v2 PDP serves one environment and its policy.
- Upgrade each service's SDK and point it at a v2 PDP in the same change. Run the v2 PDP next to the v1 PDP until every service has moved.
- Update REST API calls at any point. The v1 and v2 APIs share data, so each call can move to v2 on its own schedule.
1. Move off organization-scoped objects
v2 does not support organization-scoped schema objects. Every schema object and facts object belongs to one environment, so a change in one environment can't break another.
A v2 PDP loads the policy and data of the environment that its PDP_API_KEY belongs to, so an object that sits in another environment takes no part in that PDP's decisions. See Run the PDP. Recreate the objects you defined outside your own environments in each environment that needs them before you point a service at a v2 PDP.
- List your projects and environments. With an organization API key, list the projects, then list the environments of each project:
curl -H 'Authorization: Bearer <YOUR_ORG_API_KEY>' https://api.permit.io/v2/projects
curl -H 'Authorization: Bearer <YOUR_ORG_API_KEY>' https://api.permit.io/v2/projects/{proj_id}/envs
Replace <YOUR_ORG_API_KEY> with an organization API key, and {proj_id} with a project key or ID from the first response. Compare both lists with the projects and environments you created. An environment that nobody on your team created holds objects that v1 defined outside your environments. Take its key from the response and use that key in the paths below. If every project and environment in the list is one you created, you have no organization-scoped objects. Skip to Upgrade the PDP and SDKs.
- List the roles and users in that environment, with its project key as
{proj_id}and its own key as{env_id}:
curl -H 'Authorization: Bearer <YOUR_ORG_API_KEY>' https://api.permit.io/v2/schema/{proj_id}/{env_id}/roles
curl -H 'Authorization: Bearer <YOUR_ORG_API_KEY>' https://api.permit.io/v2/facts/{proj_id}/{env_id}/users
The roles endpoint returns an array of roles, or a paginated object with the roles in data. The users endpoint returns a paginated object with the users in data. Every entry is an object to move. If both lists are empty, that environment holds nothing to move.
- Recreate each role and user from that environment in every environment that uses it. Environments are self-contained, so adding an object to your own environment changes nothing in the environment you found it in, and your v1 PDPs keep serving the decisions they served before.
- Verify the move: run a
permit.check()call against a v2 PDP for a user and role you recreated. Compare the decision with the decision the same check returns through your v1 PDP. The two decisions match when the move is complete. - Delete the environment you didn't create, and the project that holds it if that project holds nothing else.
Deleting an environment deletes the roles, users, and other objects it holds, and a PDP that loads that environment stops getting policy and data for them. Delete it only after you recreate its objects in your own environments and a permit.check() call through a v2 PDP returns the decision you expect.
2. Upgrade the PDP and SDKs
You can run several PDPs for the same environment without extra configuration. Run a v2 PDP next to your v1 PDP, and point each service at the v2 PDP when you upgrade its SDK.
- Start a v2 PDP. Use the command you use for the v1 PDP, with the image changed to
permitio/pdp-v2. In this example, replacepermit_key_abcdefghijklmnopqrstuvwxyzwith your environment API key:
docker run -p 7766:7000 \
--env PDP_API_KEY=permit_key_abcdefghijklmnopqrstuvwxyz \
--env PDP_DEBUG=True \
permitio/pdp-v2:latest
- Upgrade each service to the v2 SDK, and set the SDK's PDP address to the v2 PDP in the same change. SDK initialization and
permit.check()calls don't change. - If the service writes data through the SDK, rewrite those calls for the v2 write API. See Rewrite SDK write calls.
- Verify the service: run a
permit.check()call through the v2 PDP, and confirm it returns the expected decision. See Run the PDP for how to check that the PDP container is running. - When every service uses the v2 SDK, stop the v1 PDP.
Rewrite SDK write calls
The v2 SDK write API is not compatible with the v1 write API. For example, in the Python SDK, a v1 user sync looks like this:
await permit.write(permit.api.sync_user(
user.customId,
user.first_name,
user.last_name,
user.email,
))
In v2, the same sync is one call:
await permit.api.sync_user(user)
user is a dict with the same fields as a user in the REST API. Only key is required. Also pass email, or first_name and last_name, so decision logs show a readable user name.
The Python SDK marks permit.api.sync_user() as deprecated and points to permit.api.users.sync(), which takes the same argument: a UserCreate object or a dict.
For the full SDK API, see the Node.js SDK quickstart and the Python SDK quickstart.
3. Update REST API calls
The v2 API reference documents v2 endpoints only, and there is no published table of v1 paths. Map each call by the object it acts on instead of by its old path:
- Identify the object type the v1 call reads or writes: user, tenant, role assignment, role, resource, or condition set.
- Find that object type in the table below, and build the path with your project and environment key.
- Replace v1 slugs and
custom_idvalues with the object's key. See Keys. - Confirm the method, the path parameters, and the request body against the object's tag in the Permit API reference.
- Run the v2 call, then read the object back with the matching
GETrequest and compare it with what the v1 call returned.
v2 endpoints by object type
| Object the call acts on | v2 method and path |
|---|---|
| Users in an environment | GET and POST /v2/facts/{proj_id}/{env_id}/users |
| One user, including a full sync of the user | GET, PUT, PATCH, and DELETE /v2/facts/{proj_id}/{env_id}/users/{user_id} |
| Roles of one user | POST and DELETE /v2/facts/{proj_id}/{env_id}/users/{user_id}/roles |
| Role assignments | GET, POST, and DELETE /v2/facts/{proj_id}/{env_id}/role_assignments |
| Tenants | GET and POST /v2/facts/{proj_id}/{env_id}/tenants, and /tenants/{tenant_id} for one tenant |
| Users of one tenant | GET and POST /v2/facts/{proj_id}/{env_id}/tenants/{tenant_id}/users |
| Resource instances | GET and POST /v2/facts/{proj_id}/{env_id}/resource_instances |
| Roles | GET and POST /v2/schema/{proj_id}/{env_id}/roles, and /roles/{role_id} for one role |
| Resources, with their actions and attributes | GET and POST /v2/schema/{proj_id}/{env_id}/resources, and /resources/{resource_id} for one resource |
| Resource roles | GET and POST /v2/schema/{proj_id}/{env_id}/resources/{resource_id}/roles |
| Condition sets (ABAC user sets and resource sets) | GET and POST /v2/schema/{proj_id}/{env_id}/condition_sets |
| Condition set rules | GET, POST, and DELETE /v2/facts/{proj_id}/{env_id}/set_rules |
| Many users or tenants in one call | POST and DELETE /v2/facts/{proj_id}/{env_id}/bulk/users and /bulk/tenants, and PUT /v2/facts/{proj_id}/{env_id}/bulk/users to replace users. See Bulk operations. |
Permission checks don't go through this API. A check goes to the PDP, not to api.permit.io. See Check permissions.
If a v1 call has no clear v2 object, ask in the Permit Slack community with the v1 path you are replacing.
Confirm the migration is complete
The migration is done when all of the following hold:
- Every project and environment that
GET /v2/projectsandGET /v2/projects/{proj_id}/envsreturn is one you created. docker pslists no v1 PDP container, and every running PDP container uses thepermitio/pdp-v2image.- A
permit.check()call through each v2 PDP returns the decision you expect for a user you migrated. - A search of your code base for
/v1/and for v1 SDK write calls returns no hits.
v2 object hierarchy
v2 objects nest in a strict hierarchy:
| Level | Represents | Example |
|---|---|---|
| Organization | Your company | Acme Corp |
| Project | One product of your company. Projects share little with each other. | billing, crm |
| Environment | One deployment stage of a project, such as production or staging | prod, staging |
Environments are self-contained. A change in the staging environment does not affect the production environment. To copy policy from one environment to another, see Create, copy, and merge environments.
Object types
Objects inside an environment are either schema objects or facts objects:
| Type | What it defines | Objects | API root |
|---|---|---|---|
| Schema objects | What can exist and what is allowed: the rules of your policy | Resources (with their actions and attributes), roles (with their permissions), condition sets | /v2/schema |
| Facts objects | What exists: the data your policy runs on | Tenants, users, role assignments, condition set rules | /v2/facts |
Organizations, projects, and environments are general objects.
General object paths
| Object | Path |
|---|---|
| Organizations | /v2/orgs |
| Projects | /v2/projects |
| Environments | /v2/projects/{project_id}/envs |
Projects are not nested under organizations in the path. Each API key belongs to a single organization, so the organization is implicit in every call. GET /v2/orgs returns the organization the API key belongs to.
Schema object paths
| Object | Path |
|---|---|
| Resources | /v2/schema/{proj_id}/{env_id}/resources |
| Resource actions | /v2/schema/{proj_id}/{env_id}/resources/{resource_id}/actions |
| Resource attributes | /v2/schema/{proj_id}/{env_id}/resources/{resource_id}/attributes |
| Roles | /v2/schema/{proj_id}/{env_id}/roles |
| Condition sets | /v2/schema/{proj_id}/{env_id}/condition_sets |
Facts object paths
| Object | Path |
|---|---|
| Users | /v2/facts/{proj_id}/{env_id}/users |
| Tenants | /v2/facts/{proj_id}/{env_id}/tenants |
| Tenant-user associations | /v2/facts/{proj_id}/{env_id}/tenants/{tenant_id}/users |
| Role assignments | /v2/facts/{proj_id}/{env_id}/role_assignments |
| Condition set rules | /v2/facts/{proj_id}/{env_id}/set_rules |
v2 path and key conventions
HTTP methods
Nested objects, such as tenant-user associations, live under the path of their parent object. v2 paths follow REST conventions:
| Request | Effect |
|---|---|
GET on an object type | Lists the objects |
GET on one object | Returns the object |
POST on an object type | Creates an object |
PATCH on one object | Changes only the fields in the request body |
PUT on one object | Replaces the object. Fields missing from the request body reset to their defaults. |
DELETE on one object | Deletes the object |
POST on a sub-path of an object | Runs an action on the object, for example POST /v2/pdps/{proj_id}/{env_id}/configs/{pdp_id}/rotate-api-key rotates the API key of a PDP |
v2 API calls authenticate with an API key in the same way as v1 calls. See Get your API key.
Keys
In v2, keys replace the v1 slugs and custom_id values. Every primary schema object and facts object has a key, and the key is required.
- Most object keys accept the characters
a-z,A-Z,0-9,_, and-, so a UUID from your own system works as a key. A user key also accepts@,.,+, and|, so an email address works as a user key. For the exact pattern of one object type, see that object's schema in the Permit API reference. - A key can't change after you create the object. A
keyin aPUTbody identifies the object to replace, and doesn't rename it. - A key is unique per object type within its namespace. For most objects the namespace is the environment. For an environment it is the project, and for a project it is the organization.
- Permit uses a key only to look up the object and to check uniqueness. Wherever a path takes a Permit ID, it also accepts the key.
For example, one environment can hold a resource with the key john and a role with the key john, and two environments can each hold a resource with the key john. One environment can't hold two resources with the key john. Likewise, two projects can each have an environment with the key prod, but one project can't have two.
Example paths
| Object | Path |
|---|---|
The document resource in the prod environment of the billing project, by key | /v2/schema/billing/prod/resources/document |
| The same resource, by ID | /v2/schema/billing/prod/resources/9014f244-9034-49a9-81ff-053a65f67ff1 |
The staging environment of the crm project, by key | /v2/projects/crm/envs/staging |
| The same environment, by ID | /v2/projects/crm/envs/1e9472f5-24d7-4b3b-b49b-39d2e8843913 |
| The same environment, by project ID and environment ID | /v2/projects/541e3523-2cdb-468a-8ca8-e7e8ccdb415d/envs/1e9472f5-24d7-4b3b-b49b-39d2e8843913 |
Get help
For migration questions, ask in the Permit Slack community.