User guide

Fulfillment

Looking for what it does rather than how to use it? Read the Fulfillment overview .

What it is

Fulfillment is how an order stops being a request and becomes something the client has received and can be billed for. Solidlio carries two order streams: client store orders, which an MSP approves and then walks through Processing → Shipped → Delivered by hand, and service orders, the single record for every service sale, which moves Draft → Approved → Completed.


Concepts

NounWhat it isID prefix
Client store orderA basket a client placed in the MSP’s store. Holds line items, totals, currency, FX snapshot and shipping addressmso_
Order line itemOne product or custom item on a store ordermoi_
Service orderThe single record for any service sale — direct, marketplace, support block, project, managed service, escalationsord_
Platform feeSolidlio’s cut, written as its own row when a service order is submitted
InvoiceProduced from a Delivered net-terms store order by the nightly invoicing jobinv_

The two lifecycles

Client store order (MspStoreOrderStatus):

PENDING_APPROVAL ──approve──> APPROVED ──> PROCESSING ──> SHIPPED ──> DELIVERED

       └──reject──> CANCELLED

Forward skips are legal (APPROVED straight to DELIVERED); backward moves are not.

  • Approve and reject are only legal from PENDING_APPROVAL.
  • Fulfillment is forward-only. From APPROVED you may jump to PROCESSING, SHIPPED or DELIVERED; from PROCESSING to SHIPPED or DELIVERED; from SHIPPED only to DELIVERED.
  • DELIVERED and CANCELLED are terminal — there is no backward edge and no post-approval cancel.

Service order (ServiceOrderStatus):

DRAFT ──submit──> APPROVED ──complete──> COMPLETED
  • submit auto-approves; there is no separate approver step.
  • complete is legal from APPROVED or IN_PROGRESS, and sets fulfillmentStatus = COMPLETED and completedAt at the same time.
  • Editing is only permitted while the order is DRAFT or PENDING_APPROVAL.

Order types and billing models (service orders)

Order type and billing model are independent fields — the type says what was sold, the billing model says how it is priced. Any type may carry any model.

  • orderTypeDIRECT_SALE, MARKETPLACE_SALE, SUPPORT_BLOCK, PROJECT, MANAGED_SERVICE, CONSULTING, TRAINING, ESCALATION
  • billingModelFLAT_RATE, PER_USER, PER_DEVICE, BLOCK_HOURS, HOURLY, PER_INCIDENT, PROJECT, HYBRID, USAGE
  • paymentTerms — defaults to NET_30

Where service orders come from

Three writers create a ServiceOrder:

WriterStatus it lands inOrder number
Scheduled billing run (contracts & recurring)APPROVEDassigned by the run
Escalation billing finalisationCOMPLETEDnone

Roles and permissions

Store order actions run behind a path-scoped guard on /store writes.

Actionplatform administratorMSP administratorMSP technicianorganization administratorpower userCUSTOMER
List store orders (whole account)
List store orders (own org only)
Read a sibling client’s store order
Approve / reject a store order
Advance fulfillment
AI triage a store order
Read service orders
Create / edit / submit / complete a service order

Notes:

  • “Store staff” is anyone at MSP technician rank or above. Staff may filter the store order list by any organization in the account. Everyone else is pinned to their own organization — a client-supplied ?organizationId= is ignored, and a sibling client’s order returns 404, not 403.
  • A non-staff caller with no organization context is rejected with Organization context required.
  • Service order reads are scoped to orders where the caller’s account is the seller or the buyer. There is no cross-account read.

Walkthrough 1 — fulfilling a client store order

MSP portal → Store → Client Orders (/msp/store/orders)

  1. Review what came in. The page opens on a filter row: All, Pending Approval, Processing, Shipped, Delivered. Header tiles summarise the orders currently loaded — awaiting approval, in flight, and their value.
  2. Approve or reject. A PENDING_APPROVAL row carries Approve and Reject buttons. Reject asks for a reason, sets the order to CANCELLED and writes Rejected: <reason> into the order notes. Both are final — an order cannot be re-opened after either.
  3. Optional: AI triage. Run triage on an order to get a stored assessment that renders inline on the row.
  4. Start processing. On an APPROVED order the action button reads Start processing. Click it and the order moves to PROCESSING.
  5. Ship it. On a PROCESSING order the button reads Mark shipped. You are prompted for a carrier and a tracking number — both optional, both capped at 120 characters. The order’s shippedAt is stamped the first time it reaches SHIPPED (or DELIVERED, if you skip ahead).
  6. Deliver it. On a SHIPPED order the button reads Mark delivered. deliveredAt is stamped once. This is the step that makes a net-terms order invoiceable.
  7. The client sees it. In the org portal under Store → Orders (/org/orders) and in the end-user portal (/user/orders), the client’s order detail renders a five-step timeline — Placed, Approved, Processing, Shipped, Delivered — plus the carrier, tracking number, ship date and delivery date as soon as you enter them.

Approval records approvedBy and approvedAt; every fulfillment call records the acting user in fulfilledBy. Rejection records the reason in the order notes but does not record who rejected it.

Walkthrough 2 — raising and closing a service order

MSP portal → Billing → Billing Overview → Service Orders (/msp/billing/orders)

  1. New order (/msp/billing/orders/new). Pick an order type and billing model, name the service, set unit price, quantity, discount and tax, and choose payment terms (default NET_30).
  2. Pricing is computed server-side. Subtotal is unitPrice × quantity; total is subtotal − discount + tax, floored at zero; the platform fee is 12% of the total, also floored at zero. A discount larger than the subtotal cannot produce a negative total or a negative fee.
  3. Save as draft, or submit in one step. Saving leaves the order in DRAFT. Passing submitForProcessing creates the order already APPROVED with its platform-fee row written, inside a single transaction — so a failure can never leave an approved order with no fee, or a fee with no order.
  4. Submit a draft (/msp/billing/orders/[id], Submit Order). The order becomes APPROVED and a PENDING platform-fee row is written against your account.
  5. Mark complete. From APPROVED or IN_PROGRESS, Mark Complete sets the status to COMPLETED, the fulfillment status to COMPLETED, and stamps completedAt.
  6. See the money. Billing → Revenue (/msp/billing/revenue) breaks revenue down by month, by order type, by service and by client, and lists every platform fee with a running total.

Walkthrough 3 — Delivered orders become invoices

This runs unattended.

  • dryRun is propagated downstream and the echo is verified: a summary that comes back without dryRun: true is reported as a failure, not as a clean dry run, because it means real clients were invoiced.
  • A Canadian order whose tax jurisdiction cannot be resolved is put on tax hold rather than invoiced at $0.

Configuration

SettingWhereEffect if unset
paymentMethod on the orderSet at checkoutDefaults to NET30. Only net-terms orders are picked up by the invoicing job
currency + fxRatesSnapshotted at checkoutDefaults to CAD. The snapshot means the order re-prices identically forever, even after rates move
paymentTerms (service order)Order formDefaults to NET_30

There is no configurable carrier list, no rate-shopping, and no label purchase. Carrier is a free-text field.


Plan tiers

Neither store order fulfillment nor service orders carry a plan gate. Every tier — MSP Free, Starter, Growth, Scale and Enterprise — has the full capability. Access is decided by role, not by plan.


Troubleshooting

What you seeWhat it means
Cannot move a PENDING_APPROVAL order to SHIPPED. Approve the order first, or it is already closed.Fulfillment only starts after approval. Approve the order, then advance it.
Cannot move a DELIVERED order to PROCESSING. …DELIVERED is terminal. There is no way back. Raise a correction through billing, not through the order.
Order cannot be rejected in current statusReject is only legal from PENDING_APPROVAL. An approved order has no cancel path.
Organization context required (403)A non-staff caller reached the store order list with no organization on the token. Re-authenticate in a portal that pins an organization.
Order not found (404) on an order you can see the number forYou are a buyer and the order belongs to a sibling client organization. This is deliberate — sibling orders are indistinguishable from non-existent ones.
Order must be in DRAFT status to submitThe service order was already submitted. Its platform fee already exists; submitting again would double-charge.
Order must be APPROVED or IN_PROGRESS to completeSubmit the draft first.
Cannot update order in current statusA service order is only editable while DRAFT or PENDING_APPROVAL.
clientOrganizationId does not belong to this accountThe organization you attributed the order to is not one of yours. Use clientName / clientEmail for an off-platform client.
buyerAccountId is only valid with a marketplaceSubscriptionId you ownA buyer account can only be derived from a marketplace subscription against a listing your account provides.
marketplaceSubscriptionId not found or not owned by this accountSame rule, other direction — the subscription is not against one of your listings.
orderType, serviceName, billingModel, and unitPriceCents are requiredThe service order form was submitted with a missing required field.

Limits and known behaviour

Read this section before promising anything to a client.

  • Fulfillment is manual and forward-only. Solidlio does not integrate with any carrier. Nothing polls a tracking number, nothing validates it, and nothing imports carrier scan events. Carrier and tracking number are free text the MSP types in, and status only moves when a person clicks.
  • There is no post-approval cancel. Once an order is APPROVED the only outward path is forward to DELIVERED. Reject is available only while the order is still PENDING_APPROVAL.
  • DELIVERED is irreversible, and it is the trigger for invoicing. Marking an order delivered by mistake will cause a net-terms order to be invoiced on the next job run.
  • Fulfillment sends no notification. Advancing an order to SHIPPED or DELIVERED writes the database and logs the event. No email and no in-app notification is raised for the client — they see the change when they next open the order.
  • Partial and split shipments are not modelled. One store order carries one carrier, one tracking number, one ship date and one delivery date. There is no per-line-item fulfillment state.
  • The separate carrier shipment register and RMA/returns register remain platform-administrator data. They are not automatically created from the client order flow. Customer and Organization mobile tracking therefore reads the authoritative, organization-scoped MspStoreOrder lifecycle instead of this disconnected register.
  • Service orders carry no approval workflow. submit auto-approves.
  • IN_PROGRESS, CANCELLED and REFUNDED are not reachable on a service order. Nothing writes them. The same is true of the IN_PROGRESS, CANCELLED and DISPUTED fulfillment statuses, and of the startedAt, ticketId, paymentStatus, paidAt, paymentIntentId and invoiceId fields — all are written at creation or never.
  • A service order cannot be attributed to a managed client account. The clientOrganizationId must be an organization inside the seller’s own account. For an MSP selling to a separately-accounted managed client, record the client as free-text clientName / clientEmail.
  • The platform fee on a manually raised service order is fixed at 12% in code. It is not read from configuration and not per-account.

Questions this guide did not answer?

Ask us. You will get a reply from someone who uses the product every day.

Book a demo Contact us

A 30-minute walkthrough against your own workflow. No slides.