# Raising a workflow event

> Raise a typed event on the run bound to an entity, from anywhere — an ordinary server function, a webhook handler, a signup step. Two spellings do it: the NAMED form states which workflow, and the INFERRED form works it out from the entity's type, which is what generic code wants when it should not have to know.

<!-- id: workflow-raise · area: workflow · stability: stable · html: https://osysharp.com/reference/workflow/raise/ -->

## Summary        {#summary}
An event is how the outside world moves a workflow along. Raising one advances the run bound to an entity — routing
to the slot in the current state that subscribes it, or to a workflow-level arm that handles it wherever the run has
got to.

There are two spellings, and the only difference is whether the call **names** the workflow:

```osy syntax
OrderFlow.RaisePayment(order, 100);       // NAMED    — you say which workflow
Workflow.Raise(order, Payment(100));      // INFERRED — worked out from the entity's type
```

Both drive the same engine, enforce the same [[workflow-authorize|`[Authorize]`]] gate, and route the same way.
Neither is a test-only surface: an ordinary server function may raise an event, and that is the normal way an app
advances a run from outside the workflow.

## Signature      {#signature}
```osy syntax
<Workflow>.Raise<Event>(entity, args…)    // named: the method is the workflow's, one per declared event
Workflow.Raise(entity, <Event>(args…))    // inferred: the event is written as a constructor call
```

`entity` is the row the run is bound to — whatever the workflow [`Tracks`](https://osysharp.com/reference/workflow/tracks/). The arguments are the
event's declared parameters, in order, and they arrive in the arm **by name**.

## Description    {#description}

### Which spelling to use   {#spellings}
Reach for the **named** form by default. It is the one that reads back as what it does — the workflow is on the page,
so anyone changing the model can find every producer of an event by searching for it, and nothing about the call
depends on facts elsewhere in the model.

Reach for the **inferred** form when the caller genuinely should not know the workflow: shared helpers, generic
plumbing, anything written against "an entity that has a workflow" rather than against one particular flow. It is
narrower than it looks — most application code knows perfectly well which workflow it is advancing, and writing that
down costs one identifier.

### ⚠ The inference needs the entity's type to have exactly ONE workflow   {#inference}
The inferred form resolves the workflow from the entity's **type**. If two workflows bind that type, there is nothing
to infer, and the compiler refuses the call rather than picking one:

```text
Workflow.Raise: more than one workflow in this unit binds 'Ticket', so the workflow cannot be inferred from the
entity — name it instead: `<Workflow>.RaiseResolve(t)`
```

This is a **compile** error on purpose, and the reason is the same as the reason the form exists. Generic code is
precisely the caller that cannot check: a helper holding somebody else's entity has no way to notice that the type
has since acquired a second workflow. So the check belongs where the whole model is in view. Adding a second workflow
to a type is a change that will name every inferred call that has just become ambiguous, and each is a one-word fix.

### Where it routes   {#routing}
Raising does not name a slot. The engine takes the event to:

- the slot in the run's **current state** that [subscribes](https://osysharp.com/reference/workflow/subscribe/) it — the ordinary case; or
- a **workflow-level** arm for it, wherever the run has got to — how a `Cancel` reaches a run in any state.

An event no slot is waiting for and no workflow-level arm handles is **refused**, not queued. To answer a specific
queued slot from a person's inbox — where the row, not the code, decides which slot is being answered — use
[`Workflow.Deposit(row, …)`](https://osysharp.com/reference/workflow/inbox-act/) instead.

### The run has to exist   {#run-required}
Both forms raise on the run **already bound** to the entity: they do not start one. If nothing has started a run for
that row, the call fails saying so — [`Workflow.Run(entity)`](https://osysharp.com/reference/workflow/run/) (or `Autostart`) is what creates it.
When a row has more than one run over its lifetime, a run still **waiting** is preferred over a finished one.

### From the run's own body — delivered when the step ends   {#own-run}
A body of the workflow — an `on` arm, a [`Remind`](https://osysharp.com/reference/workflow/remind/), a breach handler — may raise an event on the
**run it belongs to**: a reminder that asks a carrier where a parcel is and raises `Arrive` once it hears "delivered",
or an arm that forwards one event as another. The raise is **queued, and delivered as soon as the body's step ends** —
in the same call that ran the body (the same `Workflow.Settle`, the same deposit) — and it then behaves exactly like a
raise from outside: the same authorization, the same routing.

It is one step later rather than in the middle of the body because the body is itself part of a step of that run: the
run moves after the step it is in, never inside it. If the event is not one the run's state takes by then, the raise
is refused, and a `Refused` entry naming the event lands on the run's [timeline](https://osysharp.com/reference/workflow/audit/).

A body raising on a **different** run is unaffected: that raise is delivered where it is written. A body cannot
deposit into, claim, assign or release a slot of its **own** run — each is refused by name; raise an event instead.

### Authorization is the workflow's, not the caller's   {#authorization}
An event's `[Authorize]` predicate and a slot's `Candidates` are enforced by the engine, so they hold identically for
both spellings and for a raise from ordinary application code. Being able to call the function is not permission to
advance the run.

### Who a raise from a workflow body is from   {#from-a-body}
A raise written in a workflow BODY — an app's lifecycle telling a kit's run "we answered" from its `on Reply { … }` — is
from the person whose action drove that body: the same [`actor`](https://osysharp.com/reference/workflow/actor/) the body reads, and the same answer
`Workflow.Run` gives a child. The target's `[Authorize]` and `Candidates` are asked about THAT person, exactly as if they
had raised it themselves, so nothing is widened. A body driven by a timer, a reminder or a breach has no actor: its raise
into a gated event or a `Candidates` slot is refused, and an ungated event takes it — so a run that must be moved on a
timer is moved by an ungated event of its own.

On an item with runs of two workflows, `<Wf>.RaiseX(item)` reaches **that** workflow's run.

## Examples       {#examples}

An ordinary server function advancing a run — the signup / webhook shape, with the workflow named:

```osy title="named" test app=workflow-raise-named
using Osysharp.Workflow;

enum TicketStatus { Working, Closed }

entity Ticket {
  [Required, MaxLength(200)] string Title;
  TicketStatus Status = TicketStatus.Working;
  security { allow read, create, update when IsAuthenticated || IsAnonymous; }
}

workflow TicketFlow {
  Tracks  = Ticket.Status;
  Initial = Working;
  event Resolve();
  state Working {
    subscribe Resolve();
    on Resolve { goto Closed; }
  }
  terminal success Closed { }
}

void ResolveTicket(Ticket t) {
  TicketFlow.RaiseResolve(t);
}
```

The same call without naming the workflow, and with an argument the arm routes on:

```osy title="inferred" test app=workflow-raise-inferred
using Osysharp.Workflow;

enum ExpenseStatus { Filed, Approved, Rejected }

entity Expense {
  [Required, MaxLength(200)] string Memo;
  ExpenseStatus Status = ExpenseStatus.Filed;
  security { allow read, create, update when IsAuthenticated || IsAnonymous; }
}

workflow ExpenseApproval {
  Tracks  = Expense.Status;
  Initial = Filed;
  event Decide(bool approved);
  state Filed {
    subscribe Decide(bool approved);
    on Decide(bool approved) {
      when (approved) { goto Approved; }
      default { goto Rejected; }
    }
  }
  terminal success Approved { }
  terminal error   Rejected { Message = "rejected"; }
}

void DecideExpense(Expense e) {
  Workflow.Raise(e, Decide(true));
}
```

## See also       {#see-also}
- [Workflow.Run (start a workflow)](https://osysharp.com/reference/workflow/run/) — starting the run this raises on, and the awaited child-workflow form
- [subscribe](https://osysharp.com/reference/workflow/subscribe/) — the slot that waits for the event, and who may satisfy it
- [Acting on an inbox row (deposit, claim, release)](https://osysharp.com/reference/workflow/inbox-act/) — answering a specific queued slot from an inbox row instead
- [[Authorize] (event)](https://osysharp.com/reference/workflow/authorize/) — the produce-side gate on who may raise an event
- [Imported history — StartedAt, and raising an event at the time it happened](https://osysharp.com/reference/workflow/imported-history/) — raising history at the time it happened (`at:`), and `StartedAt`
