Skip to content

Part 4: Party assignment automation

Thread: API

Learn how to use rulesets to automatically define party attributes for parties with fixed identities.

Prerequisites

Learning outcomes

After completing this part, you'll be able to:

  • Understand how rulesets automate party attribute assignment
  • Define parties with fixed email addresses using rulesets
  • Simplify instance creation by removing redundant party specifications

The party definition problem

In Part 3, we manually specified party attributes when creating instances:

{
  "@parties": {
    "editor": {
      "claims": {
       "email": ["alice@example.com"]
      }
    },
    "approver": {
      "claims": {
        "email": ["bob@example.com"]
      }
    }
  },
  "content": "Q4 Financial Report"
}

This works well when parties vary per instance. But what if a party should automatically be the person who creates the instance? For example:

  • The compliance party should always have the { "email": ["support@example.com"] } claim
  • The requester should automatically be set to the person making the request

Specifying these repeatedly is redundant—the information is already in the JWT token. Party assignment automation using rulesets solves this problem by automatically extracting claims from the JWT token, requiring values to match JWT claims, or setting values for all instances.

Importantly, the automation can also be used to comply with key requirements (compliance, security, soundness of business logic), e.g. by preventing API users from creating instances and binding claims they do not carry in their JWT to specific protocol parties (thereby opening uncontrolled access or creating illegitimate contractual obligations, for instance). As a consequence, using party assignment automation can be an integral part of secure application design. In any case, controlling party bindings at protocol instantiations needs to be part of sound applications design.

Understanding rulesets

A ruleset is a YAML configuration that defines how party claims should be automatically assigned. It allows you to:

  • Set fixed attribute values for specific parties
  • Enforce that certain parties always have specific emails
  • Reduce the need to specify party attributes in API calls to create protocol instances

Example ruleset

demo.SupportRequest:
  support:
    set:
      claims:
        iss:
          - https://example.com
        email:
          - support@example.com
  requester:
    extract:
      claims:
        - iss
        - email

This ruleset says: "For all SupportRequest protocol instances in the demo package, the support party will set the iss and email claims to ["https://example.com"] and ["support@example.com]."

When the protocol should only be created by one entity, the require keyword can ensure that the claims in the JWT token match a specific one. For example, the SupportResolution protocol can be instantiated only by someone carrying the support@example.com email claim in their JWT.

demo.SupportResolution:
  support:
    require:
      claims:
        iss:
          - https://example.com
        email:
          - support@example.com

Practical example: Support request protocol

Let's model a support request system where:

  • The requester is whoever makes the request (varies per instance)
  • The support party is automatically set to support@example.com

Step 1: Define the protocol

protocol[requester, support] SupportRequest(
    var description: Text
) {
    initial state open
    state inProgress
    final state resolved
    final state closed

    @api
    permission[requester] addDetails(additionalInfo: Text) | open, inProgress {
        description = description + "\n" + additionalInfo;
    }

    @api
    permission[support] startWork() | open {
        become inProgress;
    }

    @api
    permission[support] resolve() | inProgress {
        become resolved;
    }

    @api
    permission[requester] close() | resolved {
        become closed;
    }
}

Step 2: Configure the ruleset

Create a ruleset file (e.g., rules.yml):

demo.SupportRequest:
  support:
    set:
      claims:
        iss:
          - https://example.com
        email:
          - support@example.com
  requester:
    extract:
      claims:
        - iss
        - email

This ruleset automatically extracts the iss and email claims from the JWT token and assigns them to the requester party. It also assigns the { "email": ["support@example.com"] } claim to the support party for all instances.

Step 3: Create a support request (simplified)

With the ruleset in place, Alice creates a support request:

API Call:

POST /npl/support/SupportRequest
Authorization: Bearer <ALICE_JWT_TOKEN>
Content-Type: application/json

Request Body:

{
  "@parties": {},
  "description": "Unable to access document approval feature"
}

What's happening:

  • Alice does not need to specify any party as both parties have a party automation rule
  • The ruleset automatically extracts the email claim from Alice's JWT token and assigns it to the requester party
  • The support party is automatically set to the support email

Response:

{
  "@id": "support-req-789",
  "@state": "open",
  "@parties": {
    "requester": {
      "claims": {
        "email": ["alice@example.com"]
      }
    },
    "support": {
      "claims": {
        "email": ["support@example.com"]
      }
    }
  }
}

Notice the requester party is automatically populated with Alice's issuer and email from the JWT token and the support party with the support issuer and email.

Step 4: Support team member starts work

Bob is a member of the support team and has support@example.com in his JWT token. He can start work on Alice's request:

API Call:

POST /npl/document/SupportRequest/support-req-789/startWork
Authorization: Bearer <BOB_JWT_TOKEN>
Content-Type: application/json

Request Body: no body

What's happening:

  • Bob's JWT token contains the claim "email": "support@example.com"
  • The system checks if the support party can be represented by Bob's entity—it can
  • Bob can call the startWork permission

Response:

{}

Step 5: Support resolves the issue

API Call:

POST /npl/document/SupportRequest/support-req-789/resolve
Authorization: Bearer <BOB_JWT_TOKEN>
Content-Type: application/json

Request Body: no body

Response:

{}

Step 6: Alice closes the request

API Call:

POST /npl/document/SupportRequest/support-req-789/close
Authorization: Bearer <ALICE_JWT_TOKEN>
Content-Type: application/json

Request Body: no body

Response:

{}

How ruleset automation works

When you create a protocol instance with a ruleset:

  1. Ruleset with extract: The system extracts claims from the JWT token of the user creating the instance
  2. Ruleset with set: The system assigns fixed claim values to parties
  3. Ruleset with require: The system requires specific claims to be present
  4. Validation: The system ensures all required parties are defined, from the ruleset or from the party parameters in the API call payload
  5. Instance creation: The instance is created with complete party assignments

Flow diagram

API Request → JWT Token → Ruleset Applied → Complete Party Set → Instance Created
   |              |              |                  |                    |
@parties: {}   email claim    extract claims   All parties        @id +
                extracted      + set claims     defined            @parties

Benefits of ruleset automation

Benefits of the ruleset include:

  • Reduced redundancy when setting constant party claims
  • Centralized claim setting in one place
  • Automated use of JWT token to define party claims (convenience and security)
  • Simplified client code
// Before: Must specify both parties
function createSupportRequest(description) {
  return fetch('/npl/support/SupportRequest', {
    method: 'POST',
    body: JSON.stringify({
      "@parties": {
        "requester": {
          "claims": { "email": ["alice@example.com"] }
        },
        "support": {
          "claims": { "email": ["support@example.com"] }
        }
      },
      "description": description
    })
  });
}

// After: Ruleset handles it automatically
function createSupportRequest(description) {
  return fetch('/npl/support/SupportRequest', {
    method: 'POST',
    body: JSON.stringify({
      "@parties": {},  // Both parties handled by ruleset
      "description": description
    })
  });
}

What's next?

In the next part, you'll learn about entities belonging to others, which models ownership and hierarchical relationships in NPL.