Skip to content
API Now! is currently in closed beta. We are constantly updating these guides as we release updates!

Match Lifecycle Status

The Match Lifecycle Status (lifecycleStatus) access rule is a semantic-aware attribute rule designed for entities with a lifecycle state machine. It restricts access to resources based on whether their status is permitted by an allowlist (allowedStatuses) or blocked by a denylist (deniedStatuses).

Unlike generic attribute matching, Match Lifecycle Status automatically detects the property tagged with the Status semantic module on the target entity.


  • Semantic Status Binding: Automatically binds to the entity property annotated with the Status semantic module without needing manual column selection.
  • Dual-Phase Execution:
    • Collection Actions (list, search) in FETCH Phase: Automatically injects SQL WHERE status IN (...) or WHERE status NOT IN (...) clauses into the query.
    • Single-Record Actions (read, update, delete) in POST_FETCH Phase: Inspects the hydrated record’s status in memory before executing the mutation or returning data.
  • Deny-First Evaluation: If both deniedStatuses and allowedStatuses are specified, the denylist is evaluated first for maximum security.
PropertySpecification
Rule TypelifecycleStatus
Identity Constrainingfalse (Must be paired with an identity rule in the same scenario)
Supported StrategiesAll strategies (RBAC, OrganizationRBAC, EnterpriseRBAC)
Allowed Placement LevelsExposed Entity Level (exposure), Action Level (action) (Not allowed at global API Model Level)
Execution PhasesFETCH (list, search), POST_FETCH (read, update, delete, link, unlink)

Must Be Paired with an Identity Rule

Because Match Lifecycle Status filters data based on resource state rather than caller identity, it cannot exist as the only rule in an Access Scenario. It must be paired with an identity rule (such as Allow Public, Allow Authenticated, Match User Role, or Match Resource Owner).


When configuring Match Lifecycle Status, you can specify allowlists, denylists, or both:

An array of status values permitted for this action.

  • Example: allowedStatuses: ['published']
  • Behavior:
    • list/search: Injects WHERE status IN ('published').
    • read/update: Denies (403 Forbidden) if the record status is not 'published'.

An array of status values explicitly forbidden for this action.

  • Example: deniedStatuses: ['archived', 'banned']
  • Behavior:
    • list/search: Injects WHERE status NOT IN ('archived', 'banned').
    • read/update: Denies (403 Forbidden) if the record status matches any denied value.

On collection endpoints (list and search), Match Lifecycle Status injects SQL whereIn / whereNotIn clauses during the FETCH phase.

When API clients provide query parameter filters (such as GET /articles?status=draft), the database engine combines the client filter with the authorization rule using AND logic:

SELECT * FROM articles
WHERE (status = 'draft')
AND (status IN ('in_review', 'published'))
  • Querying Permitted Statuses: If a client requests GET /articles?status=published, matching published records are returned normally (200 OK).
  • Querying Forbidden Statuses: If a client requests GET /articles?status=draft when only ['in_review', 'published'] are allowed, the query returns an empty list [] (200 OK) because no records satisfy both constraints. The engine does not throw a 403 error on list filters, preventing data enumeration.
  • Direct Record Access by ID: In contrast, if a client attempts to fetch a single forbidden record directly (GET /articles/:id), the single-record check in POST_FETCH rejects the request with 403 Forbidden.

  • Status Semantic Module: Exactly one property on the target entity must be tagged with the Status semantic module in your Data Domain (e.g. Article.status with values draft, in_review, published, archived).

Pattern 1: Multi-Tier Content Visibility (Public vs. Reviewers vs. Admins)

Section titled “Pattern 1: Multi-Tier Content Visibility (Public vs. Reviewers vs. Admins)”

Expose public content while allowing editorial staff to view review pipelines on the exact same GET /articles endpoint:

Article.list Scenarios:
├── Scenario 1 (Admin Full View):
│ └── Match User Role: ['admin'] <- Admins see all records
├── Scenario 2 (Editorial Reviewers):
│ ├── Match User Role: ['editor', 'reviewer']
│ └── Match Lifecycle Status: allowedStatuses: ['in_review', 'published']
└── Scenario 3 (Public Readers):
├── Allow Public
└── Match Lifecycle Status: allowedStatuses: ['published'] <- Public sees published only

Pattern 2: Write-Locking Finalized Records (deniedStatuses)

Section titled “Pattern 2: Write-Locking Finalized Records (deniedStatuses)”

Prevent authors from modifying invoices or documents once they reach a finalized or immutable state (e.g. paid, archived, settled):

graph TD
    subgraph "Invoice.update Endpoint"
        S1["Scenario 1: Match User Role (finance_admin)"] --> Adm["Admins can edit any invoice"]
        S2["Scenario 2: Match Resource Owner + denied: [paid, void]"] --> Owner["Author can edit unpaid invoices"]
        Blocked["Author tries to edit paid invoice"] --> Denied["403 Forbidden"]
    end

Allow authors to delete their own articles only while in draft state. Once submitted or published, deletion requires an administrator:

Article.delete Scenarios:
├── Scenario 1 (Admin Override):
│ └── Match User Role: ['admin'] <- Admins delete any article
└── Scenario 2 (Author Drafts Only):
├── Allow Authenticated
├── Match Resource Owner (author)
└── Match Lifecycle Status: allowedStatuses: ['draft'] <- Authors delete drafts only

Caller StateResource Lifecycle StatusResulting HTTP Status
Anonymous (Public Scenario)Status is in allowedStatuses (e.g. published)200 OK
Anonymous (Public Scenario)Status is NOT in allowedStatuses (e.g. draft)403 Forbidden
Author (Draft Scenario)Author tries to edit a record with status in deniedStatuses (e.g. archived)403 Forbidden
Admin (Admin Scenario)Record is in any status200 OK / 204 No Content
Resource Not FoundRecord ID does not exist404 Not Found