Sojournby CloudAlgo Request early accessEarly access Request early access
Release 1 in development, 3 of 9 sprints done. Early access is open.3 of 9 sprints done · Early access openJoin early access
Guide

Time in status in Salesforce, without an app.

Your director asks how long cases sit in each status. Here is what standard Salesforce already tells you, how to build the rest with a Flow, and where that build stops.

The question sounds simple: how many hours did cases spend in New, in Working, and in Waiting on customer last month, and which team held them longest. Salesforce can tell you every time a status changed. The duration is the gap between one change and the next, and that gap is the part you have to build.

This guide uses Case and its Status field. The same steps work for Lead status, Opportunity stage, or a picklist on a custom object.

What standard Salesforce gives you

Field history tracking

In Setup, under Field History Tracking, you turn on history for an object and choose its fields: up to 20 standard and custom fields per object. Each change to a tracked field adds an entry to the record's History related list with the date, time, nature of the change, and who made it. Salesforce stores the entries in a history object, such as CaseHistory, whose rows carry the field name, the old value, and the new value.

A row records a moment. Nothing in it says how long the old value lasted, so a history report lists changes, not stays. To get time in a status from history you subtract each row's date from the next row's date for the same case, outside the report or in code.

Opportunity stage duration

Opportunities are the exception. The Opportunity History report type has From Stage and To Stage columns, and opportunity reports offer a Stage Duration column: the number of days the opportunity was in the stage. Two limits matter. It counts days, and when an opportunity returns to a stage, Salesforce's own example shows the report keeping only the first visit: 2 days in Proposal, then Negotiation, then 3 more days in Proposal reports 2 days, not 5. And it covers stage on opportunities only, not owner, and not other objects.

Case Age

Case reports include an Age column. For an open case it is the time from creation to now; for a closed case, from creation to closing. You can show it in days, hours, or minutes, and it does not take into account holidays in the case's business hours. You can also enable a Business Hours Age column, which ages the case by your business hours instead of calendar days; it appears on the standard case reports, not on custom report types built on Case.

Both measure the whole life of the case. Neither says how much of that time was spent in Waiting on customer.

Build it yourself with a record-triggered Flow

The do-it-yourself version stamps the time of each status change on the case and, on the next change, adds the minutes since that stamp to a field for the status the case just left. It needs no code and no app.

Fields on Case

FieldTypeHolds
Status_Changed_At__cDate/TimeWhen the current status began
Minutes_in_New__cNumberTotal minutes spent in New
Minutes_in_Working__cNumberTotal minutes spent in Working
Minutes_in_Waiting__cNumberTotal minutes spent in Waiting on customer

Add one Number field for every Status value you report on.

The Flow

  1. Create a record-triggered Flow on Case that runs when a record is updated, optimized for Fast Field Updates. This kind of flow runs before the save, and changes it makes to {!$Record} are saved with the record, so no Update Records element is needed.
  2. Set the entry condition to Status Is Changed True, and run the flow every time a record is updated and meets the condition. Is Changed is always false when a record is created, which is why the formula below falls back to the case's created date.
  3. Add a formula resource, MinutesInStay, of type Number with two decimal places:
    (
      {!$Flow.CurrentDateTime}
      - BLANKVALUE({!$Record.Status_Changed_At__c}, {!$Record.CreatedDate})
    ) * 1440
    Subtracting one Date/Time from another returns days as a decimal, so multiplying by 1,440 gives minutes. $Flow.CurrentDateTime is the moment the flow runs the element.
  4. Add a Decision on {!$Record__Prior.Status}, the value the case had just before this save, with one outcome per status: Equals New, Equals Working, and so on.
  5. In each outcome, an Assignment sets that status's field to a formula, so a blank field counts as zero. For New:
    BLANKVALUE({!$Record.Minutes_in_New__c}, 0) + {!MinutesInStay}
    That is one formula per status.
  6. After the Decision, one Assignment sets {!$Record.Status_Changed_At__c} to {!$Flow.CurrentDateTime}.

Cases that already exist have no stamp, so their first stay would count from the day the case was created. Before you activate the flow, set Status_Changed_At__c on open cases to the activation time with a data import, and treat that first stay as partial.

One row per stay instead

Fields on the case answer "how long did this case spend in each status". To average by status across a team in one report, store each stay as a row: a custom object such as Case Status Stay, with a lookup to Case, the status, entered and left date/times, and minutes. A Fast Field Updates flow can only update the triggering record, so the row comes from a second record-triggered flow optimized for Actions and Related Records, which runs after the save.

Where it breaks

  • Calendar time only. The formula subtracts clock time, so a case that waits from Friday evening to Monday morning counts the weekend. The formula function reference lists no function that reads your org's business hours, and Salesforce's own sample business-hours formula assumes a fixed 9 to 5 day in one time zone, and does not account for daylight saving time, holidays, or your configured business hours. Business hours need Apex: the BusinessHours.diff method returns the milliseconds between two date/times for a set of business hours, and an @InvocableMethod makes that callable from the flow.
  • No history before the day you build. The flow sees only the changes made after it is active. Field history works the same way: Salesforce tracks from the moment you turn it on and records nothing earlier.
  • A field per status, per object. A new status value needs a new field, a new outcome, and a new formula. Lead and Opportunity each need their own fields and their own flow.
  • Owner time is a second build. Time with each owner, user or queue, needs its own stamp, its own fields or rows, and its own flow, and it cannot answer "how long did Tier 2 hold cases that were waiting on the customer" unless the two builds share rows.
  • One report per object. Case minutes live on Case and Lead minutes on Lead, so there is no single list of stays to report on across objects.
  • History has limits too. Field history covers up to 20 fields per object. Salesforce keeps it for up to 18 months, and up to 24 months through the API. Field Audit Trail, sold with Salesforce Shield or as Field Audit Trail licenses, keeps history until you delete it and tracks up to 200 fields per object, with access through the API only.

None of this makes the Flow wrong. For one object, one status field, and a team that works around the clock, it is a good answer. Each item above is where admins usually end up adding fields, flows, or Apex.

What Sojourn does instead

Sojourn is a managed package that records each stay as a row, an Interval, with the value, the owner, and business and calendar minutes on every row. It does this today for status-style picklists and Owner on Case, Lead, Opportunity, and any custom object, with business hours set per org, object, field, or queue.

Release 1 adds Backfill from your existing field history, Goals and breach flags, Live values refreshed every 15 minutes, and 20 report templates and 4 dashboards. Release 1 is in development, and early access is open.

Early access

See your own cases in business hours.

Install Sojourn in a sandbox, choose what to track, and see the first Intervals the same day.

Read by the two people building Sojourn. A written reply within one working day.

Or email sales@cloudalgo.com

Sources

Every Salesforce fact on this page comes from these official pages.

  1. Field History Tracking Overview
  2. Track Object Field History
  3. CaseHistory object reference
  4. Opportunity History Report
  5. Tips for Working with Opportunity Reports
  6. Create Salesforce Report on Duration of Opportunity Stage
  7. Fields Available for Case Reports
  8. Enabling Business Hours Age in Case Report
  9. Before-Save Record-Triggered Flows
  10. Record-Triggered Flow Considerations
  11. How Entry Conditions Work in Record-Triggered Flows
  12. Flow Operators in Decision, Wait, and Collection Filter Elements
  13. Global Variables Resource ($Record and $Record__Prior)
  14. $Flow Global Variables Resource
  15. Flow Operators in Assignment Elements
  16. Using Date, Date/Time, and Time Values in Formulas
  17. Formula Operators and Functions by Context
  18. Sample Date Formulas
  19. BusinessHours Class
  20. InvocableMethod Annotation
  21. Field Audit Trail