Pumler Learn · Diagramming

PlantUML Sequence Diagrams: Syntax, Examples, and an Online Editor

Learn PlantUML sequence diagram syntax with practical examples of activation bars, return messages, loops, and alternatives. Try them in our online sequence diagram editor.

A sequence diagram is what you reach for when “service A calls service B” no longer provides enough detail. It shows who communicates, in what order, and what happens when the happy path stops being happy.

PlantUML turns that interaction into compact text. You name the participants, write messages from top to bottom, and let the renderer handle lifelines and spacing. That makes the result easy to revise, review in Git, and keep beside the behavior it explains.

Want to try the syntax first? Open the editable example below in Pumler, change a message, and watch the preview update. It is a small but complete PlantUML sequence diagram—no local setup or manual layout required.

PlantUML basic-order-sequence.puml Open in editor (opens in new tab)
@startuml
actor Customer
participant "Order API" as Orders
participant "Inventory service" as Stock

Customer -> Orders: Place order
Orders -> Stock: Reserve items
Stock --> Orders: Reservation confirmed
Orders --> Customer: Order accepted
@enduml
Rendered result
Sequence diagram where a customer places an order, the order API reserves inventory, and confirmation messages return to the API and customer.
A complete first sequence diagram: three participants, four messages, and a clear top-to-bottom order.

By the end of this tutorial, you will be able to declare participants and aliases, choose useful arrow styles, show activation bars, close the most recent activation with return, describe alternative/optional paths and loops. That is enough syntax for most API and service-flow conversations.

How a UML sequence diagram works

A UML sequence diagram puts participants across the top and gives each one a vertical lifeline. Messages appear as horizontal arrows. Their vertical position supplies the order: a message lower on the page happens after a message above it.

That sounds almost suspiciously simple, and that is the point. A sequence diagram is good at answering a narrow set of questions:

  • Who starts the interaction?
  • Which component owns each decision or side effect?
  • In what order do calls and responses happen?
  • Which alternatives, optional steps, or repeated steps matter?
  • When does a participant actively handle a request?

It is less useful for showing a database schema, static dependencies, or exact deployment topology. Trying to smuggle all three into one sequence diagram usually produces a very accurate picture of nobody wanting to read it.

Every editable PlantUML diagram in this guide uses a complete envelope:

@startuml
' participants and messages go here
@enduml

@startuml begins the diagram and @enduml closes it. Pumler preserves this standard PlantUML source in the editor and renders it as a sequence diagram.

Participants, actors, and aliases

PlantUML can infer participants from message lines, but explicit declarations are better for a maintained diagram. They fix the left-to-right order and make each role obvious before the first message appears.

Use actor for a person or external role and participant for a service, component, or other collaborator:

actor Customer
participant Checkout
participant Payment

Names with spaces need quotes. An alias gives that long display name a short identifier for later lines:

participant "Order API" as Orders
participant "Payment gateway" as Payment

Orders -> Payment: Create authorization

The text in quotes appears in the diagram; Orders and Payment are the identifiers used in source. Aliases keep message lines readable and survive a display-name change without a diagram-wide rewrite.

Choose participant names at one abstraction level. Browser, Order API, and Payment service belong together. Button, Kubernetes cluster, and Finance department probably do not. If two names need a paragraph to explain why they share a diagram, the scope is already drifting.

Messages and arrow syntax

The basic PlantUML sequence diagram syntax for a message is:

Sender -> Receiver: Message label

The colon separates the arrow from the message label. Prefer labels that describe intent—Reserve items, Authorize card, Load order — instead of vague labels such as Call or Request.

You will commonly see two right-pointing arrow styles:

Orders -> Stock: Reserve items
Stock --> Orders: Reservation confirmed

The solid -> style is commonly used for a request, while dotted --> is commonly used for a response. That is a visual convention, not semantics enforced by PlantUML. The renderer does not know whether a dotted message is a return, event, callback, or request. Your labels and team conventions carry that meaning.

Arrows can point left too. Reverse-arrow syntax makes the direction explicit when the sender is on the right:

actor Customer
participant Checkout
participant Payment

Checkout <-- Payment: Authorization result
Customer <-- Checkout: Order confirmed

Do not write every low-level call simply because it exists. A useful sequence tells one story. If the question is how checkout handles payment failure, cache reads and metrics calls are probably noise unless they affect that outcome.

Activation bars: activate, ++, and return

An activation bar shows that a participant is actively executing or waiting within a call. PlantUML gives you explicit commands and compact message shortcuts.

The explicit form is straightforward:

Client -> Orders: Place order
activate Orders
' Orders is active here
deactivate Orders

activate Orders opens an activation on the Orders lifeline. deactivate Orders closes that named activation. This form is readable when the active region spans several messages or when you want the source to state the lifecycle plainly.

The shorthand ++ activates the receiver on the same line as a message:

Orders -> Fraud ++: Check risk

Importantly, ++ works independently of the arrow style. The shorthand pattern often described as --> ++ combines a dotted arrow with receiver activation; in a complete message, the receiver appears between them: Fraud --> Orders ++: Request order context. The dotted line still has no enforced “response” meaning.

PlantUML activation-stack.puml Open in editor (opens in new tab)
@startuml
actor Client
participant "Order API" as Orders
participant "Fraud service" as Fraud

Client -> Orders: Place order
activate Orders
Orders -> Fraud ++: Check risk
Fraud --> Orders ++: Request order context
return Order context
return Risk approved
Orders --> Client: Order accepted
deactivate Orders
@enduml
Rendered result
Sequence diagram with nested activation bars. A client calls an order API, the API activates a fraud service, the fraud service activates the API for order context, and two return messages unwind the nested calls.
Explicit and shorthand activation syntax can coexist; return closes the most recently opened activation first.

Follow the activation stack in order:

  1. activate Orders opens the outer Orders activation.
  2. Orders -> Fraud ++ opens an activation on Fraud.
  3. Fraud --> Orders ++ opens a nested activation on Orders.
  4. The first return sends a message back from the most recently activated participant and closes that nested Orders activation.
  5. The second return closes the Fraud activation and returns control to the earlier Orders activation.
  6. deactivate Orders closes the remaining explicit activation.

In other words, return behaves like unwinding a stack: it targets the most recent open activation, adds the labeled return message, and closes that activation. It does not mean “send a response to whichever participant looks visually convenient.” With nested activations, the order matters.

PlantUML also recognizes -- as the shorthand that deactivates the sender on a message. It can make compact diagrams even shorter, but explicit return lines are often easier for beginners to trace because the response label and stack change appear in one readable statement.

Alternatives with alt and else

Real interactions branch. PlantUML represents mutually exclusive paths with an alt combined fragment:

alt payment approved
    Payment --> Checkout: Authorization token
else payment declined
    Payment --> Checkout: Decline reason
end

The text after alt and else describes the condition for each branch. Indent the messages inside each branch with four spaces. PlantUML does not require that whitespace, but consistent indentation makes the source much easier to scan and review.

Use alt when the paths compete: approved or declined, found or missing, authorized or rejected. Close the whole fragment with end.

PlantUML checkout-branches.puml Open in editor (opens in new tab)
@startuml
actor Customer
participant Checkout
participant Payment

Customer -> Checkout: Submit order
Checkout -> Payment: Authorize card

alt payment approved
    Payment --> Checkout: Authorization token
    Checkout --> Customer: Order confirmed
    opt email address provided
        Checkout --> Customer: Send receipt
    end
else payment declined
    Payment --> Checkout: Decline reason
    Checkout --> Customer: Payment failed
end
@enduml
Rendered result
Checkout sequence diagram with approved and declined payment alternatives, plus an optional receipt on the approved path when an email address is present.
Combined fragments keep the happy path and its meaningful alternatives in one readable scenario.

This example keeps the branch outcome close to the payment call that creates it. Inside the approved branch, it nests an opt fragment:

opt email address provided
    Checkout --> Customer: Send receipt
end

opt represents behavior that may happen but has no competing else path. It is effectively a one-branch conditional. If the absent case needs messages of its own, use alt instead.

A common mistake is putting every HTTP error into a separate branch. Include a branch when it changes the interaction, ownership, or outcome relevant to the reader. If three errors all produce the same retry message, one retryable failure branch may be more honest and far more readable.

Repetition with loop

Use loop when the same interaction repeats and the repetition itself matters:

loop for each pending reminder
    Scheduler -> Notifications: Prepare reminder
    Notifications -> Email: Send email
end

The text after loop should state the bound or condition: for each item, up to 3 attempts, or while status is pending. A label such as repeat adds almost no information.

PlantUML reminder-loop.puml Open in editor (opens in new tab)
@startuml
autonumber
participant Scheduler
participant "Notification service" as Notifications
participant "Email gateway" as Email

loop for each pending reminder
    Scheduler -> Notifications: Prepare reminder
    Notifications -> Email: Send email
    Email --> Notifications: Delivery accepted
end

note right of Notifications
    Failed deliveries enter the retry queue.
end note
@enduml
Rendered result
Numbered sequence diagram where a scheduler loops through pending reminders, asks a notification service to prepare each one, and the service sends it through an email gateway.
A bounded loop, automatic message numbers, and one useful note document repeated work without drawing every iteration.

The example also uses two small features that make longer diagrams easier to discuss.

autonumber numbers messages in display order. Those numbers are useful in design reviews: “What happens if step 3 times out?” is quicker than “That arrow around the middle, no, the other one.” They are derived from source order, so inserting a message updates the sequence automatically.

A note attaches context without inventing another participant or message:

note right of Notifications
    Failed deliveries enter the retry queue.
end note

Keep notes rare and local. If the diagram needs a wall of prose to explain every arrow, put the detailed policy beside the diagram and let the sequence remain scannable.

How to create a sequence diagram from scratch

The quickest reliable workflow is not “write perfect PlantUML from memory.” It is a short modeling loop.

1. Write one scenario as a sentence

Start with a concrete case: “A customer submits an order; checkout authorizes payment; a decline returns an error.” Avoid combining account creation, checkout, fulfillment, cancellation, and refunds in the first draft.

2. List only the participants needed for that scenario

Name the initiating actor, the component receiving the request, and the collaborators that change the result. Use aliases for long labels. Three to five participants is a comfortable starting range.

3. Add the happy-path messages from top to bottom

Write Sender -> Receiver: Intent one line at a time. Read the finished source aloud. If you have to move backward in time to explain it, reorder the messages.

4. Add only meaningful control flow

Use alt for outcomes that send the scenario down different paths, opt for one optional block, and loop for meaningful repetition.

5. Add activation bars when ownership is unclear

Reach for activate and deactivate when a long-lived operation matters. Use ++, -- and return for nested calls where the stack is easy to follow. Then check that every open activation is deliberately closed.

6. Open it in an online sequence diagram editor

Paste the source into Pumler or use any Open in editor link in this guide. The live preview catches syntax errors immediately and makes iteration faster than exporting an image after every change. Once the flow is correct, keep the text as the source of truth and generate the visual from it.

With a text-based sequence diagram editor, reordering steps means moving lines, renaming a participant means editing its declaration, and the layout updates automatically.

Common PlantUML sequence diagram mistakes

Leaving spaces in an unquoted participant name

Write participant "Order API" as Orders, not participant Order API. The alias becomes the stable source identifier.

Treating dotted arrows as enforced return semantics

--> changes appearance. It does not automatically bind the message to an earlier call or close an activation. Use a clear label and, when activation state matters, use return or an explicit deactivation.

Closing the wrong activation

With nested ++ calls, return unwinds the most recent activation first. If the output looks surprising, trace the activations as a stack. Use explicit activate and deactivate when that makes ownership clearer.

Forgetting end

alt, opt, and loop blocks all need end. Multi-line notes use end note. Consistent indentation makes a missing closer much easier to spot.

Modeling an entire system in one scenario

Large sequence diagrams usually fail gradually: one extra participant, one extra branch, one “important” background job at a time. Split by reader question. A checkout success/failure flow and a refund-retry flow can share participants without sharing a canvas.

Using implementation-shaped labels

POST /v1/orders can be useful in an API contract diagram. In an architecture discussion, Place order may communicate the intent better. Pick the level that matches the review and keep it consistent.

What to learn next

This tutorial covers the core PlantUML sequence diagram syntax: participants and aliases, messages, activation bars, return messages, and combined fragments. You also learned how to use alt, opt, loop, notes, and automatic numbering. PlantUML supports more controls and styling options for sequence diagrams than we need here.

When a real scenario calls for more, use the official PlantUML sequence diagram documentation as the reference. Add features because they clarify the interaction, not because the syntax catalog is there.

To create your own sequence diagram now, open one of the examples above in Pumler and replace its participants and messages with your own. Start with the happy path, render early, and add branches only after the basic story reads cleanly.

That is enough to go from a blank editor to a useful sequence diagram today—and enough restraint to keep the result useful next month.