---
title: Resources
project: Authaz
updated: 2026-04-29T17:34:55.738Z
---


# Resources

The **Resource Catalog** (under *Authorization → Manage → Resources*) is where you declare *every* thing in your application that has permissions attached. Roles and policies can only reference permissions that exist here — the catalog is the source of truth.

```bash
# Define a resource
curl -X PUT https://your-app.authaz.io/api/v1/applications/{appId}/resources \
  -H "X-API-Key: $AUTHAZ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "resources": [
      {
        "resource": "invoices",
        "description": "Customer invoices and billing records.",
        "actions": ["create", "read", "update", "delete", "send", "archive"]
      },
      {
        "resource": "customers",
        "description": "Customer records.",
        "actions": ["create", "read", "update", "delete"]
      }
    ]
  }'
```

Resource names + actions become permission strings: `invoices:create`, `customers:read`, etc. The dashboard's resource editor previews these as you type.

## Where it lives

**Dashboard → Application → Authorization → Resources.** The page shows every resource with its actions in an editable table. A counter at the top tells you the total — a sane catalog usually has 5–20 resources.

## What goes in a resource

| Field | What it is |
|-------|-----------|
| **resource** (key) | A short, stable identifier in lower-snake-case: `users`, `invoices`, `api_keys`. Don't change this casually — it's referenced from every policy. |
| **description** | Human-readable. Shows in the policy editor when picking permissions. |
| **actions** | The verbs valid for this resource: `read`, `update`, `delete`, etc. Each action becomes a permission `resource:action`. |

The dashboard helps with action names — common ones (`create`, `read`, `update`, `delete`, `list`) auto-complete. Custom actions (`invoices:send`, `users:invite`) are fine; just keep them short and verb-shaped.

## Modelling tips

**Granularity.** One resource per *concept*, not per database table. If "invoices" and "invoice_line_items" are always touched together, they're one resource as far as authorization is concerned.

**Actions.** Stick to a small standard vocabulary plus the few domain verbs that genuinely matter:

```
create  read  update  delete  list      ← the standard CRUD-ish set
send    archive  publish  approve  invite  ← verbs your business cares about
```

If you find yourself adding `update_status_to_pending`, `update_status_to_approved`, etc., collapse them — the action is `update`; the *which-status-is-allowed* logic belongs in your business code.

**Naming.** `resource:action` strings show up in audit logs, error messages, JWT claims, and permission checks. They're load-bearing identifiers — pick names you won't want to refactor.

## Direct resource grants (ReBAC)

For "this user can read *this specific document*" cases, skip roles and grant directly on a resource instance:

```bash
curl -X POST https://your-app.authaz.io/api/v1/authorization/relationships \
  -H "X-API-Key: $AUTHAZ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "user_01h...",
    "resource": "documents",
    "resourceId": "doc_01h...",
    "relation": "viewer"
  }'
```

Now this user can do anything `viewer` allows on `doc_01h...`, and only on that document. The check:

```bash
curl -X POST https://your-app.authaz.io/api/v1/authorization/check \
  -H "X-API-Key: $AUTHAZ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "user_01h...",
    "resource": "documents",
    "resourceId": "doc_01h...",
    "action": "read"
  }'
```

Returns `{ "allowed": true }` even if the user has no roles in this application.

### Relations

A relation is the named connection between a user and a resource. Authaz ships with a small set of built-in relations:

| Relation | Allowed actions (default) |
|----------|--------------------------|
| `owner` | every action on this resource type |
| `editor` | `create`, `read`, `update` |
| `viewer` | `read`, `list` |
| `commenter` | `read`, `comment` (if `comment` is a valid action) |

You can define custom relations per resource — the catalog editor has a *Relations* tab where each relation picks which actions it implies. Keep this small; most apps need 2–3 relations max.

### Listing relationships

```bash
# Everyone with a relation on a resource
curl 'https://your-app.authaz.io/api/v1/authorization/relationships?resource=documents&resourceId=doc_01h...' \
  -H "X-API-Key: $AUTHAZ_API_KEY"

# Every resource a user has a relation on
curl 'https://your-app.authaz.io/api/v1/authorization/relationships?userId=user_01h...&resource=documents' \
  -H "X-API-Key: $AUTHAZ_API_KEY"
```

The dashboard's *Access Explorer* surfaces both views interactively — useful when investigating "why can this person see this thing?".

### Bulk writes

For migrating an existing app to Authaz, batch the writes:

```bash
curl -X POST https://your-app.authaz.io/api/v1/authorization/relationships/bulk \
  -H "X-API-Key: $AUTHAZ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "writes": [
      { "userId": "u1", "resource": "documents", "resourceId": "d1", "relation": "owner"  },
      { "userId": "u1", "resource": "documents", "resourceId": "d2", "relation": "editor" },
      { "userId": "u2", "resource": "documents", "resourceId": "d1", "relation": "viewer" }
    ]
  }'
```

Up to 5,000 writes per call.

## When to use each style

| Situation | Roles + Policies | Direct grants (ReBAC) |
|-----------|:----------------:|:---------------------:|
| User-type-based access ("Admins can do X") | ✓ | |
| Per-record access ("Alice can see this one document") | | ✓ |
| Same access for everyone in a tenant | ✓ | |
| Sharing UI ("Invite people to this project") | | ✓ |
| Role hierarchies that change rarely | ✓ | |
| Heavy mutation (millions of grants) | | ✓ (Zeratul is built for this) |

Use both. Most real apps start role-based, then add direct grants when a sharing/collaboration feature lands.

## Next steps

- [Policies](./policies.md) — bundles built from the actions you defined here.
- [Roles](./roles.md) — composed from those policies.
- [Access Explorer](./access-explorer.md) — debug a check and see whether the answer came from a role or a direct grant.
