Skip to content
How-to guide

How to Suggest an Actor for a Case

Use this guide when you believe an actor belongs on a case and you are not the one who decides. A suggestion goes to the CASE_MANAGER, which presents it to the Case Owner for a decision (ADR-0026, ADR-0029). You finish with the suggested actor either invited or the recommendation declined, and with the outcome reported back to you.


Prerequisites

  • A running Vultron instance you can drive. The demo containers are the quickest route — see Tutorial: Run the Receive-Report Demo for the two steps that get you a container shell.
  • An actor identity on that instance, holding the role each step names.
  • Working knowledge of the ActivityStreams verb, object, and target shape, summarized in Vultron and ActivityPub.
  • Working knowledge of the case model: the objects a case is made of, in The Case Model, and who is allowed to change them, in The CASE_MANAGER and the Case Ledger. Where a guide's messages are case-scoped, they route through the CASE_MANAGER and their results appear on the case ledger.

  • A case you participate in. Any participant can suggest an actor. The CASE_MANAGER refuses a suggestion from an actor that is not a participant, and forwards nothing to the Case Owner.

  • The suggested actor's Uniform Resource Identifier (URI).
  • The CASE_MANAGER's actor URI.

If you hold the Case Owner role and the decision is yours alone, invite the actor directly instead — see How to Invite an Actor to a Case.


The exchange

The sequence diagram below shows the full round trip, from recommendation to invitation or the Case Owner's Reject, which the CASE_MANAGER ledgers like an Accept. The recommender never contacts the Case Owner or the invitee directly.

---
title: Suggesting an Actor for a Case (ADR-0026 CaseActor-routed)
---
sequenceDiagram
    actor A as Recommender
    participant CA as CASE_MANAGER
    actor B as Case Owner
    actor D as Invitee
    Note over A: Recognize that Actor should be invited
    A ->> CA: Offer(object=Actor, target=Case)
    activate CA
    Note over CA: Record in ledger, assign default roles
    CA ->> B: Offer(object=CaseParticipant{actor,roles}, target=Case, origin=Offer.id)
    activate B
    alt Accept Suggestion
        B ->> CA: Accept(object=Offer(CaseParticipant))
        CA ->> A: AcceptActorRecommendation
        CA ->>+ D: Invite(CaseStub + embargo + roles)
        note over D: Respond to invitation (not shown)
        deactivate D
    else Reject Suggestion
        B ->> CA: Reject(object=Offer(CaseParticipant))
        CA ->> A: RejectActorRecommendation
    end
    deactivate B
    deactivate CA

Recommend the actor

The formal message set does not name this exchange: all three activities fall under the General Inquiry (GI) umbrella, which is a placeholder rather than a description — see Why GI expands.

  1. Send Offer(Actor) to the CASE_MANAGER with the recommended actor as its object and the case as its target.

    Suggest actor to case

    Recommends an actor to the Case Manager for the case named by the target.

    • When… a Participant suggests an actor that the case should include
    • Send: Offer(Actor)[target=VulnerabilityCase]
    • Send to: the case manager
    • Sent by: a participant
    • Distinguished by: sender is not the case owner
    • Reference: Suggest actor to case
    Example activity
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Offer",
      "id": "urn:uuid:9583f623-6de2-4ca6-8e28-960df723382b",
      "name": "https://vultron.example/users/finndervul Offer Coordinator LLC to urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "content": "I'm recommending we add Coordinator LLC to the case.",
      "to": "https://vultron.example/case-actors/vendorco-case-actor",
      "actor": "https://vultron.example/users/finndervul",
      "target": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "object": {
        "@context": "https://www.w3.org/ns/activitystreams",
        "type": "Organization",
        "id": "https://vultron.example/organizations/coordinator",
        "name": "Coordinator LLC",
        "published": "2026-10-23T20:17:06+00:00",
        "updated": "2026-10-23T20:17:06+00:00",
        "inbox": {
          "@context": "https://www.w3.org/ns/activitystreams",
          "type": "OrderedCollection",
          "id": "https://vultron.example/organizations/coordinator/inbox",
          "items": []
        },
        "outbox": {
          "@context": "https://www.w3.org/ns/activitystreams",
          "type": "OrderedCollection",
          "id": "https://vultron.example/organizations/coordinator/outbox",
          "items": []
        }
      }
    }
    
  2. Wait. The CASE_MANAGER records the recommendation on the ledger, assigns default roles, and forwards a transformed offer to the Case Owner.

    Offer case participant

    Forwards the recommendation as a proposed participant record, carrying the original recommendation's ID and the default roles it assigned.

    • When… the Case Manager presents a suggested actor to the Case Owner
    • Send: Offer(CaseParticipant)[target=VulnerabilityCase]
    • Send to: a participant
    • Sent by: the case manager
    • Reference: Offer case participant
    Example activity
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Offer",
      "id": "urn:uuid:5d4ebc93-ff95-4007-80d4-456ab3330e0e",
      "name": "https://vultron.example/case-actors/vendorco-case-actor Offer https://vultron.example/organizations/coordinator from urn:uuid:345d3ce6-98bc-4807-af6e-3e44d47cd1af to urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "content": "Recommending Coordinator LLC for case participation (roles: VENDOR). Origin: urn:uuid:345d3ce6-98bc-4807-af6e-3e44d47cd1af",
      "to": [
        "https://vultron.example/organizations/vendorco"
      ],
      "actor": "https://vultron.example/case-actors/vendorco-case-actor",
      "target": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "origin": "urn:uuid:345d3ce6-98bc-4807-af6e-3e44d47cd1af",
      "object": {
        "id": "https://vultron.example/organizations/coordinator#participant",
        "type": "CaseParticipant",
        "name": "https://vultron.example/organizations/coordinator",
        "published": "2026-10-23T20:17:08+00:00",
        "updated": "2026-10-23T20:17:08+00:00",
        "attributedTo": "https://vultron.example/organizations/coordinator",
        "caseRoles": [
          "vendor"
        ],
        "participantStatuses": [
          {
            "id": "urn:uuid:8d5a2b5e-26b3-4b24-9f55-e28fda9370ca",
            "type": "ParticipantStatus",
            "name": "START vf",
            "context": "https://vultron.example/organizations/coordinator#participant",
            "published": "2026-10-23T20:17:08+00:00",
            "updated": "2026-10-23T20:17:08+00:00",
            "attributedTo": "https://vultron.example/organizations/coordinator",
            "rmState": "START",
            "vfState": "vf",
            "caseEngagement": true,
            "cvdRole": [
              "VENDOR"
            ],
            "@context": "https://certcc.github.io/Vultron/ns/context.jsonld"
          }
        ],
        "embargoConsents": [],
        "joined": true,
        "@context": "https://certcc.github.io/Vultron/ns/context.jsonld"
      }
    }
    
  3. Read the outcome from the CASE_MANAGER's answer to your Offer(Actor). Its inReplyTo names your recommendation. An Accept(Offer(Actor)) means the suggested actor is invited, or was already invited or a participant.

    Accept actor recommendation

    Accepts the recommender's suggestion, with inReplyTo naming it. The Case Manager sends it once the Case Owner approves, or at once when the suggested actor is already invited or has joined.

    • When… the Case Manager tells a recommender that the actor it suggested will be invited
    • Send: Accept(Offer(Actor)[target=VulnerabilityCase])
    • Send to: a participant
    • Sent by: the case manager
    • Reference: Accept actor recommendation
    Example activity
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Accept",
      "id": "urn:uuid:718f5ffa-2079-42c3-bb0d-3082301c7f4c",
      "name": "https://vultron.example/case-actors/vendorco-case-actor Accept https://vultron.example/users/finndervul Offer Coordinator LLC to urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0 to urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "inReplyTo": "urn:uuid:704631c1-29d5-4a5a-bd48-c7695c4c5d05",
      "content": "We're accepting your recommendation to add Coordinator LLC to the case. We'll reach out to them shortly.",
      "to": "https://vultron.example/users/finndervul",
      "actor": "https://vultron.example/case-actors/vendorco-case-actor",
      "target": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "object": {
        "@context": "https://www.w3.org/ns/activitystreams",
        "type": "Offer",
        "id": "urn:uuid:704631c1-29d5-4a5a-bd48-c7695c4c5d05",
        "name": "https://vultron.example/users/finndervul Offer Coordinator LLC to urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
        "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
        "published": "2026-10-23T20:17:08+00:00",
        "updated": "2026-10-23T20:17:08+00:00",
        "content": "I'm recommending we add Coordinator LLC to the case.",
        "to": "https://vultron.example/case-actors/vendorco-case-actor",
        "actor": "https://vultron.example/users/finndervul",
        "target": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
        "object": {
          "@context": "https://www.w3.org/ns/activitystreams",
          "type": "Organization",
          "id": "https://vultron.example/organizations/coordinator",
          "name": "Coordinator LLC",
          "published": "2026-10-23T20:17:06+00:00",
          "updated": "2026-10-23T20:17:06+00:00",
          "inbox": {
            "@context": "https://www.w3.org/ns/activitystreams",
            "type": "OrderedCollection",
            "id": "https://vultron.example/organizations/coordinator/inbox",
            "items": []
          },
          "outbox": {
            "@context": "https://www.w3.org/ns/activitystreams",
            "type": "OrderedCollection",
            "id": "https://vultron.example/organizations/coordinator/outbox",
            "items": []
          }
        }
      }
    }
    

    A Reject(Offer(Actor)) means the Case Owner declined the suggestion.

    Reject actor recommendation

    Declines the recommender's suggestion, with inReplyTo naming it. The suggested actor is not invited.

    • When… the Case Manager tells a recommender that the Case Owner declined the actor it suggested
    • Send: Reject(Offer(Actor)[target=VulnerabilityCase])
    • Send to: a participant
    • Sent by: the case manager
    • Reference: Reject actor recommendation
    Example activity
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Reject",
      "id": "urn:uuid:d72f06dd-6eb9-4228-9f2f-a4f477b04104",
      "name": "https://vultron.example/case-actors/vendorco-case-actor Reject https://vultron.example/users/finndervul Offer Coordinator LLC to urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0 to urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "inReplyTo": "urn:uuid:80376211-48c4-41de-bb12-a389d5557f92",
      "content": "We're declining your recommendation to add Coordinator LLC to the case. Thanks anyway.",
      "to": "https://vultron.example/users/finndervul",
      "actor": "https://vultron.example/case-actors/vendorco-case-actor",
      "target": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "object": {
        "@context": "https://www.w3.org/ns/activitystreams",
        "type": "Offer",
        "id": "urn:uuid:80376211-48c4-41de-bb12-a389d5557f92",
        "name": "https://vultron.example/users/finndervul Offer Coordinator LLC to urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
        "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
        "published": "2026-10-23T20:17:08+00:00",
        "updated": "2026-10-23T20:17:08+00:00",
        "content": "I'm recommending we add Coordinator LLC to the case.",
        "to": "https://vultron.example/case-actors/vendorco-case-actor",
        "actor": "https://vultron.example/users/finndervul",
        "target": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
        "object": {
          "@context": "https://www.w3.org/ns/activitystreams",
          "type": "Organization",
          "id": "https://vultron.example/organizations/coordinator",
          "name": "Coordinator LLC",
          "published": "2026-10-23T20:17:06+00:00",
          "updated": "2026-10-23T20:17:06+00:00",
          "inbox": {
            "@context": "https://www.w3.org/ns/activitystreams",
            "type": "OrderedCollection",
            "id": "https://vultron.example/organizations/coordinator/inbox",
            "items": []
          },
          "outbox": {
            "@context": "https://www.w3.org/ns/activitystreams",
            "type": "OrderedCollection",
            "id": "https://vultron.example/organizations/coordinator/outbox",
            "items": []
          }
        }
      }
    }
    

Accept either answer only from the CASE_MANAGER, and change nothing on receipt. The CASE_MANAGER ledgered the answer, and your replica takes it from the ledger.

The forwarded offer carries the original recommendation's ID in its origin field, so the Case Owner can trace the request back to you.


Decide on a recommendation

As Case Owner, answer the forwarded offer, addressing the reply to the CASE_MANAGER and not to the recommender.

  • If the actor should join, send Accept(Offer(CaseParticipant)), with the forwarded Offer(CaseParticipant) as its object. The CASE_MANAGER then invites the actor.

    Accept case participant offer

    Accepts the proposed participant record. The Case Manager then invites the suggested actor.

    • When… the Case Owner approves a suggested actor
    • Send: Accept(Offer(CaseParticipant)[target=VulnerabilityCase])
    • Send to: the case manager
    • Sent by: the case owner
    • Reference: Accept case participant offer
    Example activity
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Accept",
      "id": "urn:uuid:b1e59138-1d05-4585-a4ec-8a55e029b630",
      "name": "https://vultron.example/organizations/vendorco Accept https://vultron.example/case-actors/vendorco-case-actor Offer https://vultron.example/organizations/coordinator from urn:uuid:7cac2759-5e2a-4785-a8ff-383778fbb824 to urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0 to urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "content": "Accepting recommendation to add Coordinator LLC to the case.",
      "to": [
        "https://vultron.example/case-actors/vendorco-case-actor"
      ],
      "actor": "https://vultron.example/organizations/vendorco",
      "target": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "object": {
        "@context": "https://www.w3.org/ns/activitystreams",
        "type": "Offer",
        "id": "urn:uuid:c4314e5f-2fe6-4060-8101-725af6c22175",
        "name": "https://vultron.example/case-actors/vendorco-case-actor Offer https://vultron.example/organizations/coordinator from urn:uuid:7cac2759-5e2a-4785-a8ff-383778fbb824 to urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
        "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
        "published": "2026-10-23T20:17:08+00:00",
        "updated": "2026-10-23T20:17:08+00:00",
        "content": "Recommending Coordinator LLC for case participation (roles: VENDOR). Origin: urn:uuid:7cac2759-5e2a-4785-a8ff-383778fbb824",
        "to": [
          "https://vultron.example/organizations/vendorco"
        ],
        "actor": "https://vultron.example/case-actors/vendorco-case-actor",
        "target": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
        "origin": "urn:uuid:7cac2759-5e2a-4785-a8ff-383778fbb824",
        "object": {
          "id": "https://vultron.example/organizations/coordinator#participant",
          "type": "CaseParticipant",
          "name": "https://vultron.example/organizations/coordinator",
          "published": "2026-10-23T20:17:08+00:00",
          "updated": "2026-10-23T20:17:08+00:00",
          "attributedTo": "https://vultron.example/organizations/coordinator",
          "caseRoles": [
            "vendor"
          ],
          "participantStatuses": [
            {
              "id": "urn:uuid:900d68a1-68be-49d9-95ed-a825345d0f04",
              "type": "ParticipantStatus",
              "name": "START vf",
              "context": "https://vultron.example/organizations/coordinator#participant",
              "published": "2026-10-23T20:17:08+00:00",
              "updated": "2026-10-23T20:17:08+00:00",
              "attributedTo": "https://vultron.example/organizations/coordinator",
              "rmState": "START",
              "vfState": "vf",
              "caseEngagement": true,
              "cvdRole": [
                "VENDOR"
              ],
              "@context": "https://certcc.github.io/Vultron/ns/context.jsonld"
            }
          ],
          "embargoConsents": [],
          "joined": true,
          "@context": "https://certcc.github.io/Vultron/ns/context.jsonld"
        }
      }
    }
    
  • If it should not, send Reject(Offer(CaseParticipant)) with the same object. No invitation is sent.

    Reject case participant offer

    Rejects the proposed participant record. The Case Manager tells the recommender.

    • When… the Case Owner declines a suggested actor
    • Send: Reject(Offer(CaseParticipant)[target=VulnerabilityCase])
    • Send to: the case manager
    • Sent by: the case owner
    • Reference: Reject case participant offer
    Example activity
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Reject",
      "id": "urn:uuid:a7548af7-546a-4feb-bcb3-29d41c41fa35",
      "name": "https://vultron.example/organizations/vendorco Reject https://vultron.example/case-actors/vendorco-case-actor Offer https://vultron.example/organizations/coordinator from urn:uuid:ee1e7335-2f65-4b91-bce5-96badb4414ba to urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0 to urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "content": "Declining recommendation to add Coordinator LLC to the case.",
      "to": [
        "https://vultron.example/case-actors/vendorco-case-actor"
      ],
      "actor": "https://vultron.example/organizations/vendorco",
      "target": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "object": {
        "@context": "https://www.w3.org/ns/activitystreams",
        "type": "Offer",
        "id": "urn:uuid:5a751154-d8cd-477d-886f-6d202c649b47",
        "name": "https://vultron.example/case-actors/vendorco-case-actor Offer https://vultron.example/organizations/coordinator from urn:uuid:ee1e7335-2f65-4b91-bce5-96badb4414ba to urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
        "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
        "published": "2026-10-23T20:17:08+00:00",
        "updated": "2026-10-23T20:17:08+00:00",
        "content": "Recommending Coordinator LLC for case participation (roles: VENDOR). Origin: urn:uuid:ee1e7335-2f65-4b91-bce5-96badb4414ba",
        "to": [
          "https://vultron.example/organizations/vendorco"
        ],
        "actor": "https://vultron.example/case-actors/vendorco-case-actor",
        "target": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
        "origin": "urn:uuid:ee1e7335-2f65-4b91-bce5-96badb4414ba",
        "object": {
          "id": "https://vultron.example/organizations/coordinator#participant",
          "type": "CaseParticipant",
          "name": "https://vultron.example/organizations/coordinator",
          "published": "2026-10-23T20:17:08+00:00",
          "updated": "2026-10-23T20:17:08+00:00",
          "attributedTo": "https://vultron.example/organizations/coordinator",
          "caseRoles": [
            "vendor"
          ],
          "participantStatuses": [
            {
              "id": "urn:uuid:0bfb170f-43df-44fb-ae61-ddbb6ff7bc6e",
              "type": "ParticipantStatus",
              "name": "START vf",
              "context": "https://vultron.example/organizations/coordinator#participant",
              "published": "2026-10-23T20:17:08+00:00",
              "updated": "2026-10-23T20:17:08+00:00",
              "attributedTo": "https://vultron.example/organizations/coordinator",
              "rmState": "START",
              "vfState": "vf",
              "caseEngagement": true,
              "cvdRole": [
                "VENDOR"
              ],
              "@context": "https://certcc.github.io/Vultron/ns/context.jsonld"
            }
          ],
          "embargoConsents": [],
          "joined": true,
          "@context": "https://certcc.github.io/Vultron/ns/context.jsonld"
        }
      }
    }
    

The CASE_MANAGER acts on a decision only when the Case Owner sent it. It refuses a reply from any other sender, invites no one, and tells the recommender nothing. The actor it invites is the one named in the Offer(CaseParticipant) it forwarded, never the one named in your reply. It also refuses an Accept for an actor that has already joined or has closed its record, and it commits and sends nothing for it. The same holds for an actor the case has removed. It also refuses a reply that names an Offer(CaseParticipant) it never forwarded, or a recommendation it holds no record of, and it commits and sends nothing for it.

Replying to the recommender skips the record

The CASE_MANAGER is the single writer for the case ledger. A decision sent straight back to the recommender is never committed, so no replica learns of it and no invitation follows.


Verify

What you sent What to confirm
Offer(Actor) The recommendation appears as a ledger entry.
Accept(Offer(CaseParticipant)) The recommender holds an Accept(Offer(Actor)), and the suggested actor holds an Invite.
Reject(Offer(CaseParticipant)) The recommender holds a Reject(Offer(Actor)), and no Invite was sent.

See it end to end

Try it: vultron-demo suggest-actor

vultron-demo suggest-actor

Or with Docker Compose:

DEMO=suggest-actor docker compose -f docker/docker-compose.yml run --rm demo

The scenario runs both outcomes: the Case Owner accepts one suggestion and rejects another.


Further reading