Skip to content

Integration  ·  Finance

How to build a revenue recognition feed

Revenue is recognised from a spreadsheet built off invoice totals, and the auditor asks how the allocation was derived. Below is the model behind a feed that answers, the prompts that build it, and the parts that only bite once it is live.

Built with Tray Headless

  1. System Salesforce CPQ
  2. Step Extract obligations
  3. Step Allocate on SSP
  4. Step Build schedules
  5. System NetSuite
Also Snowflake

Performance obligations travel from the contract instead of being inferred from an invoice, and every allocation records the inputs behind it.

The short answer

What is a revenue recognition feed?

A revenue recognition feed comes down to four things: performance obligations carried from the contract instead of inferred from invoice totals, allocation against standalone selling prices with the inputs recorded, schedules generated per obligation instead of per invoice, and a reconciliation from bookings through to recognised revenue every period. Most of these come apart on starting from the invoice. An invoice is a billing event and a performance obligation is an accounting one, and they rarely correspond.

What matters here

  • Start from the contract, not the invoice. Billing and recognition are different events that rarely correspond.
  • Record the allocation inputs. An auditor asks how a figure was derived, and a spreadsheet formula is not an answer.
  • Generate schedules per obligation. One contract can hold several with different patterns and dates.
  • A modification is a new allocation, not an adjustment. Getting that wrong is a restatement.
  • Reconcile bookings to billings to revenue every period, and explain the differences rather than plugging them.

Who this is for

You run revenue accounting or finance systems. Recognition runs from a spreadsheet, the allocation logic lives in somebody's head, and each audit is a reconstruction exercise.

How it works in practice

The path from a contract being signed to revenue being recognised correctly.

  1. 1

    Performance obligations come from the contract

    Each distinct promise, with its own delivery pattern, not a line on an invoice.

  2. 2

    Standalone selling prices are applied

    From a maintained table, with the basis recorded per allocation.

  3. 3

    The transaction price is allocated across obligations

    Proportionally, with every input stored for reconstruction.

  4. 4

    A schedule is generated per obligation

    Ratable over term, at a point in time, or on usage, each with its own dates.

  5. 5

    Modifications trigger a new allocation

    Prospective or cumulative catch-up per the standard, never an ad hoc adjustment.

  6. 6

    Bookings, billings and revenue reconcile every period

    With deferred revenue explained rather than balanced.

What the feed is made of

Four parts, and the second is what makes an audit survivable.

Obligations from the contract

Each distinct promise with its own pattern. An invoice is a billing event and tells you nothing about how revenue should be recognised.

Recorded allocation

The standalone prices used, the version of the table, and the arithmetic. An auditor asks how, and a formula in a spreadsheet is not an answer.

Schedules per obligation

Ratable, point in time or usage based, each with its own start and end. One contract routinely holds several.

A full reconciliation

Bookings to billings to recognised revenue to deferred, with differences explained rather than plugged.

The Tray Headless prompts

Paste these into Claude Code or Codex with the Tray Headless plugin installed. Each stage runs on its own. The systems named in them are the worked example rather than a requirement, and every prompt says so.

Once per project, run /tray-workflows:set-workspace to pick the workspace these build in. Point it at a sandbox first.

  1. 1

    First, take obligations from the contract

    An invoice is a billing event, not an accounting one.

    Headless skills build-workflow

    Use build-workflow. The systems in play are Salesforce CPQ and
    NetSuite, or whatever we run in those seats.
    
    I need contract line items with products, quantities, prices and term
    dates, and from the ERP the revenue arrangement and schedule objects.
    
    Extract performance obligations from the contract, not from invoices:
    each distinct promise, its delivery pattern, its start and end, and
    whether it is delivered over time or at a point in time.
    
    A twelve month subscription invoiced annually in advance is one
    obligation recognised over twelve months and one invoice. A professional
    services engagement invoiced monthly might be one obligation recognised
    on completion. Starting from the invoice gets both wrong.
  2. 2

    Allocate against standalone prices, and record the working

    An auditor asks how the figure was derived.

    Headless skills build-workflow tray-patterns

    Use build-workflow. Hold standalone selling prices as a versioned table
    with the basis for each: observed price, adjusted market assessment, or
    cost plus margin.
    
    Allocate the transaction price across obligations in proportion to
    standalone price, and store on every allocation: which prices were used,
    which version of the table, the resulting percentages, and the amount
    allocated per obligation.
    
    Handle a discount deliberately. Where it is not attributable to a
    specific obligation, allocate it proportionally, and where the contract
    says otherwise, follow the contract and record that it did.
    
    The stored working is the point. Recomputing an allocation two years
    later against today prices produces a different number, and being unable
    to reproduce the original is what turns an audit question into a
    finding.

    Version the price table. Allocations are computed against the table as it was, and without versioning the whole history becomes irreproducible.

  3. 3

    Generate a schedule per obligation

    One contract routinely holds several with different patterns.

    Generate a schedule per obligation rather than per contract:
    
      Ratable over the term, monthly, with proration on partial periods
      At a point in time, on delivery or acceptance, with the trigger
      recorded
      On usage, from metered consumption
      On milestones, where the contract defines them
    
    Each carries its own start and end, which frequently differ within one
    contract. A subscription starting in March and an implementation
    completing in May belong to the same contract and to different schedules
    entirely.
    
    Post the periodic recognition and the deferred balance, with each entry
    linking back to the obligation and the allocation that produced it.
  4. 4

    Handle modifications as new allocations

    Getting this wrong is a restatement.

    Headless skills tray-gotchas

    Use tray-gotchas, then handle contract modifications properly:
    
      A mid-term upgrade adding a distinct obligation at its standalone price
      is treated as a separate contract
      A modification that is not distinct requires a reallocation across the
      remaining obligations with a cumulative catch-up
      A downgrade or partial termination requires reversing the unrecognised
      portion, not the recognised one
      A renewal is a new contract with a new allocation, not a continuation
    
    Never adjust a schedule directly. Every change produces a new allocation
    linked to the original, with the reason recorded.
    
    This is the part where an error becomes a restatement rather than a
    correction, and it is worth being slow about.
  5. 5

    Reconcile bookings through to revenue

    Deferred revenue should be explained, never balanced.

    Reconcile every period:
    
      Bookings against contract value entered
      Contract value against total allocated, which must agree exactly
      Allocated against scheduled
      Scheduled against posted revenue
      Opening deferred plus billings minus revenue against closing deferred
    
    Every difference must be explainable, and none may be plugged. A
    balancing entry in revenue recognition is the entry an auditor finds.
    
    Report per period: revenue by obligation type, deferred balance and its
    ageing, contracts with no allocation, allocations with no schedule, and
    modifications processed with their treatment.
    
    Contracts with no allocation is the one to watch. Each is revenue nobody
    is recognising.
  6. 6

    Validate, then hand the price table to revenue accounting

    Because standalone prices change and the table is theirs.

    Run the per-step schema checks and the whole-workflow audit before this
    touches production. Run parallel with the existing spreadsheet for two
    periods and reconcile every difference before trusting it.
    
    Then open the same workflow in Tray Build so revenue accounting can
    maintain the standalone price table and the obligation templates in the
    visual canvas.

What it connects to

The contract defines the obligations, the ERP holds the ledger, and the working has to survive both.

Salesforce CPQ

Read contract lines, products, term dates and any ramp, which is where obligations come from.

Reads

NetSuite

Create revenue arrangements and schedules, post recognition and hold the deferred balance.

Reads and writes

Salesforce

Read the closed opportunity and write the arrangement reference back for the account team.

Reads and writes

Snowflake

Hold allocations with their inputs and versions, so a figure from two years ago is reproducible.

Writes

Slack

Alert on a contract with no allocation or a reconciliation difference before close.

Writes

Same build, other stacks

The design does not change if you run something else in one of these seats. The same prompts build it against Microsoft Dynamics 365, SAP S/4HANA, Google BigQuery, Microsoft Teams, HubSpot or Oracle.

Named systems are the ones most teams run, not the only ones that work. Each is an authentication in your Tray workspace, referenced by name, so the workflow never holds a credential. Where we have a connector page, the name links to it.

Running it in production

This produces reported revenue. It is audited every year and restated when wrong.

It runs on the platform, not on somebody's machine

Allocation and scheduling run as contracts land, and the period reconciliation runs on the close calendar, not when somebody opens a file.

Every allocation is reproducible

The prices used, the table version and the arithmetic stored together. Recomputing against today prices gives a different answer, and that gap is what becomes a finding.

Credentials are held by the platform, never hardcoded

Posting revenue entries is the most consequential write in the finance stack. Scoped narrowly, held in your workspace, rotated separately.

Revenue accounting own the price table

Standalone prices and obligation templates open in Tray Build, versioned, owned by the people who defend the policy.

Nothing is plugged

Every reconciliation difference is explained. A balancing entry in revenue recognition is precisely the entry an auditor looks for.

Questions people ask

Why start from the contract rather than the invoice?

Because an invoice is a billing event and a performance obligation is an accounting one, and they rarely correspond. An annual invoice for a twelve month subscription is one invoice and twelve months of revenue.

Why record the allocation inputs?

Because recomputing an allocation two years later against current standalone prices produces a different number. Being unable to reproduce the original is what turns an audit question into a finding.

Why a schedule per obligation?

Because one contract routinely holds several with different patterns and dates. A subscription starting in March and an implementation completing in May are the same contract and entirely different schedules.

How should modifications be handled?

As a new allocation linked to the original, prospective or with a cumulative catch-up depending on whether the added goods are distinct. Adjusting a schedule directly is how a correction becomes a restatement.

What should never happen in the reconciliation?

A plug. Every difference between bookings, billings, revenue and deferred must be explained, because a balancing entry in revenue recognition is exactly what an auditor is looking for.

Last reviewed September 2026.