# Stream an AroFlo site

> Land an AroFlo site's jobs, quotes, invoices, timesheets and job costs in your warehouse a few times a day, read with the site's own API keys.

An AroFlo stream lands an AroFlo site's jobs, quotes, invoices, supplier bills, timesheets and the labour and materials booked to each job in your warehouse, a few times a day. Each run reads the jobs, invoices and bills that changed since the last run, and the last few weeks of time and costs again, so late changes land.

AroFlo is a **published source**, like [Postgres](https://docs.sanda-os.com.au/streams/postgres) and [Bite](https://docs.sanda-os.com.au/streams/bite): sanda wrote the code that reads it and runs it as its own, so there is no connector for sam to write or for anyone to approve. A site administrator copies the site's API keys from AroFlo, you choose what to read, and you set a schedule.

:::note
AroFlo's API terms ask for AroFlo's agreement before a service like sanda reads a site's data, so AroFlo is in the catalogue only once sanda has switched it on for your console. If **Choose a source** doesn't list it, ask sanda support.
:::

:::note
AroFlo's terms don't allow its data, or anything made from it, to be sent to an advertising network, so pushes to Google Ads and Meta refuse these tables. That holds for a view or a table built from them too: sanda follows a push's selection back to the tables it reads.
:::

## Before you start

- **A site administrator has the API keys.** AroFlo calls them uEncoded, pEncoded, orgEncoded and the API Secret Key, and keeps them under **Site Administration · Settings · General · AroFlo API**. AroFlo shows the API Secret Key once, when it is generated, so whoever set up the site's other integrations may have it saved.
- **Don't generate new keys for sanda if another integration uses them.** Generating new keys stops every integration that has the old ones, such as an accounting sync. Use the keys you have.
- **sanda reads as the AroFlo user the keys belong to.** AroFlo's API access is turned on for one user, and sanda sees what that user may see. If someone turns that user's API access off, runs stop until a site administrator turns it on again.
- **AroFlo counts every request against the site.** AroFlo allows a site 2,000 requests a day, counted from midnight in Melbourne, and that day is shared by everything that reads the site: an accounting sync, other apps, and sanda. sanda leaves the last 500 of them for the rest.
- **One stream reads one site.** Each AroFlo site is connected once and read by one stream, so its allowance is spent once. Tick everything you want on that one stream.

## Connect a site

:::steps
1. **Open Pull.** Go to **Data · Streams**, open the **Pull** tab and press the **AroFlo** card under **Choose a source**. The new pull's editor opens on **Connect an AroFlo site** when none is connected yet. Once a site is connected, **Connect a site** beside AroFlo under **Connected** adds another.
2. **Copy the four values** from **Site Administration · Settings · General · AroFlo API** in AroFlo into **uEncoded**, **pEncoded**, **orgEncoded** and **API Secret Key**. **Name** is optional: leave it empty and sanda names it after the site's first business unit.
3. **Press Check and connect.** sanda asks AroFlo for the site's business units with the keys before it keeps them. If AroFlo refuses them, the form says why and nothing is kept.
4. **The site is listed** under AroFlo in **Connected**, and chosen in the editor if you started from a new stream.
:::

sanda keeps the uEncoded, pEncoded and API Secret Key encrypted, and they are never shown again, in the console or to sam. orgEncoded stays in view, because it names the site and is worth nothing without the other three. The keys are checked once a connect, which spends one of the day's requests.

**Type them again**, beside the site under **Connected**, replaces the keys after they change in AroFlo: when someone generates new ones, or when the API user's username changes (uEncoded changes with it). It asks for uEncoded, pEncoded and the API Secret Key, and keeps the site's orgEncoded, so the stream reads the same site. **Disconnect** forgets the keys, and the site's stream waits until they are typed again.

## Make a stream

:::steps
1. **Start it.** Press the **AroFlo** card under **Choose a source**, or **New pull** and then **AroFlo**.
2. **Choose when it runs.** **When it runs** comes first. An AroFlo stream runs at most once every 60 minutes, and every 6 hours suits most sites: it keeps jobs and costs fresh through the day and leaves most of the site's allowance to its other integrations. See [when a stream runs](https://docs.sanda-os.com.au/streams/schedules).
3. **Name it.** Its tables are named after it: a stream called `aroflo` lands jobs in `raw.aroflo__tasks`.
4. **Choose the site**, and, if your workspace has more than one warehouse, the warehouse it lands in. Both are chosen once.
5. **Choose what it reads.** Everything is ticked. Untick what you don't need: each resource spends at least one request a run, or a day for the lists read once a day.
6. **Read history from** is optional: the date the first run starts from. Leave it empty to read the last 365 days.
7. **Days read again** is how far back each run reads time, costs, quotes and orders again. Leave it empty for 30 days.
8. **Press Save pull.** Saving reads nothing yet: press **Run now**, or wait for its schedule.
:::

A first run on a busy site can take more than a day. Each day's runs stop once the site's allowance is down to the reserve, and the first run after midnight in Melbourne carries on from the page it reached. The stream's page says so under the run.

## What it reads

| Tick | Lands in | Each run reads |
|---|---|---|
| **Tasks** | `raw.<stream>__tasks` | The tasks that changed since the last run |
| **Invoices** | `raw.<stream>__invoices` | The invoices that changed since the last run |
| **Supplier bills** | `raw.<stream>__bills` | The bills that changed since the last run |
| **Users** | `raw.<stream>__users` | The users that changed since the last run |
| **Locations** | `raw.<stream>__locations` | The locations that changed since the last run |
| **Inventory items** | `raw.<stream>__inventory` | The items that changed since the last run |
| **Assets** | `raw.<stream>__assets` | The assets that changed since the last run |
| **Timesheets** | `raw.<stream>__timesheets` | The last 30 days again |
| **Labour on jobs** | `raw.<stream>__task_labours` | The last 30 days again |
| **Materials on jobs** | `raw.<stream>__task_materials` | The last 30 days again |
| **Expenses on jobs** | `raw.<stream>__task_expenses` | The last 30 days again |
| **Payments** | `raw.<stream>__payments` | The last 30 days again |
| **Schedules** | `raw.<stream>__schedules` | The last 30 days again, and every booking ahead |
| **Quotes** | `raw.<stream>__quotes` | The last 30 days again |
| **Purchase orders** | `raw.<stream>__purchase_orders` | The last 30 days again |
| **Work orders** | `raw.<stream>__work_orders` | The last 30 days again |
| **Clients** | `raw.<stream>__clients` | The whole list, once a day |
| **Suppliers** | `raw.<stream>__suppliers` | The whole list, once a day |
| **Projects** | `raw.<stream>__projects` | The whole list, once a day |
| **Business units** | `raw.<stream>__business_units` | The whole list, once a day |
| **Task types** | `raw.<stream>__task_types` | The whole list, once a day |
| **Substatuses** | `raw.<stream>__substatuses` | The whole list, once a day |
| **Tracking centres** | `raw.<stream>__tracking_centres` | The whole list, once a day |

The editor marks the resources that land people's names and contact details: every one but inventory items, assets, business units, task types, substatuses and tracking centres. Most name the AroFlo user who did the work, booked it, received it or last changed it; tasks, quotes and locations carry a contact's name and phone, and quotes, invoices and purchase orders an address.

### Tasks

AroFlo calls a job a task. Each run reads the tasks whose record changed since the last run, oldest change first, starting 10 minutes before the newest change the last run saw. A task comes with its cost totals (materials, labour, expenses and hours), its substatus and its custom fields, so job profit and work in progress need no other table. A task read twice is still one row, and one that hasn't changed costs nothing.

The first run reads the tasks changed since **Read history from**. A task deleted in AroFlo keeps its row until everything is read again: **Load everything again** reads every task changed since that date, and marks the ones AroFlo no longer lists with `_deleted_at`.

sanda reads tasks in the order AroFlo lists them by when they changed, each page starting a little before where the last one ended. If AroFlo ever lists them out of that order, the run stops before it lands that page and says so, rather than carry on and miss a task.

### Invoices, bills and the other changed records

Invoices, supplier bills, users, locations, inventory items and assets are read as tasks are: the records that changed since the last run, 10 minutes back. An invoice comes with its lines and its task, and a bill with its lines and the tasks they are costed to.

Their first run reads the invoices made since **Read history from**, the bills changed since it, and every user, location, item and asset, because the rest join to them, including the ones nobody has changed in years. A first run that takes days doesn't lose what changed meanwhile: the next run reads again from when the first one began.

A record deleted from one of these lists keeps its row: AroFlo doesn't say when one goes.

### Time, costs, quotes and orders

Timesheets, labour, materials and expenses on jobs, payments, schedules, quotes, purchase orders and work orders don't say when they last changed, so each run reads the last **Days read again** of them again, by their own date: the day worked, used or paid, the day a booking starts, or the day the quote or order was made. A late timesheet or a corrected labour line lands on the next run. A timesheet, expense, payment, booking, quote or order deleted in AroFlo inside those days is marked with `_deleted_at`. Labour and material lines deleted since those days began arrive anyway, as AroFlo keeps them, with `deleteddatetime` filled in and `deleted` set: AroFlo writes `1` on a deleted labour line (`0` on one that stands) and `true` on a deleted material line (`false` on one that stands). Filter on `deleteddatetime` to treat both the same. Schedules read every booking ahead as well, and a cancelled booking is marked once its day comes.

A quote is read again only while it was made within **Days read again**, so an older quote keeps the status it had when it was last read. Raise **Days read again** if your quotes stay open longer, at the cost of more requests each run.

### The lists read once a day

Clients, suppliers, projects, business units, task types, substatuses and tracking centres are read whole once a day, whatever the stream's schedule, because they change rarely and AroFlo counts every page. A row the list no longer has is marked `_deleted_at` and kept. **Load everything again** reads them on its next run.

### Left out

- **Quote line items.** AroFlo lists them quote by quote, which would spend a request for every quote. The quote's own totals (cost, sell, profit and margin) are read.
- **Stock levels.** The quantity of each item each holder has on hand is not read; the inventory items are.
- **Documents, photos and notes.** AroFlo's links to a document expire soon after it makes them, so a link landed in a table would not open.
- **AroFlo's list of changed records.** It doesn't count against the day's requests, but it names records without what is in them, and reading each one back is a request of its own. The windows above read the same changes a page at a time.
- **The payer's bank account on a payment.** AroFlo fills in the account number, branch and bank code for a cheque or a direct debit. sanda leaves them out on purpose: a report of what was paid doesn't need them, and a payer's account number is not something to keep. The bank's name, the cheque number and the reference are read.

## What lands in your warehouse

Every field becomes a column and the whole record is kept in `_record`, as for every [pull](https://docs.sanda-os.com.au/streams/in#where-the-records-land). AroFlo writes nearly every value as text, so a few things are worth knowing when you model them:

- **Numbers, flags and dates are text**, as AroFlo sends them: a cost is `83.0000`, a flag `true` or `false`, a date `2026/10/01` and a time `2026/10/01 08:00:00`. Cast them in a view. An empty date arrives as an empty or one-space value.
- **Clients' and suppliers' creation times are timestamps.** AroFlo writes `dateinserted`, `datetimeinserted` and `datecreated` on clients and suppliers differently from everything else (`2018-06-21 10:59:01.8`), so they land as timestamps without a time zone, in the site's own time.
- **Times whose field name ends in `utc` are UTC.** The others, such as a task's `requestdatetime`, are the site's own time.
- **Ids are text**, AroFlo's own (`JSZaSyRQLDAgCg==`), and a job's number is `jobnumber`. A labour line's `task` names its task by `taskid`, an invoice's `task` the task it bills.
- **Nested details are JSON**: a task's `client`, `tasktotals` and `customfields`, an invoice's or a bill's `lines`, a timesheet's `user` and `worktype` each land as a JSON column of their own.

## Change a stream

**Edit**, on the stream's page, changes everything but the site and the warehouse. Something you take off keeps its table, with what it landed. **Days read again** takes effect on the next run. A new **Read history from** date takes effect the next time everything is read from the start: press **Load everything again** on the stream's page. It asks first, because reading everything again can spend a day's allowance. **Run now** asks first too when the stream's last run started less than 60 minutes ago.

## Remove AroFlo data

AroFlo's terms ask that a site's data be deleted once the site stops letting sanda read it, or when AroFlo asks. To remove a site's data from sanda:

:::steps
1. **Delete the site's pull**: **Delete pull**, at the foot of the stream's page. Its tables stay in your warehouse until you remove them.
2. **Disconnect the site** under AroFlo in **Connected**. To stop the keys working in AroFlo as well, a site administrator turns off the API user's access there, or generates new keys, which stops every other integration that uses the old ones too.
3. **Drop what you built from it.** Remove the views and modelled tables in [Modelling](https://docs.sanda-os.com.au/modelling) that read the stream's tables.
4. **Ask sanda support to drop the stream's tables** (`raw.<stream>__tasks` and the rest), naming the stream. Landing tables can't be dropped from the console.
:::

## When something goes wrong

| What you see | What to do |
|---|---|
| AroFlo's daily allowance for this site is nearly spent, so the run stopped and left the rest for the site's other integrations | Nothing to do: runs wait until midnight in Melbourne, saying when they ask again, and the first run after carries on. If it happens every day, untick what you don't need, lower **Days read again**, or run the stream less often |
| AroFlo's daily allowance for this site is spent, so the run stopped | As above. Something else read the site heavily today |
| AroFlo refused the API keys sanda reads the site with | The keys changed in AroFlo (new keys, or a new username for the API user), or the API user's access was turned off. The stream waits, rather than failing again and again. Press **Type them again** beside the site under **Connected** and type the current ones, once a site administrator has checked the API user's access |
| AroFlo says the API user these keys belong to has no AroFloAPI access | A site administrator turns AroFloAPI access on for that user in AroFlo. The keys stay connected, and the next run carries on. If the stream paused itself meanwhile, press **Resume** on its page |
| AroFlo didn't understand sanda's request, sanda asked AroFlo for something without a filter, AroFlo didn't list tasks in the order they changed, or an answer was larger or slower than AroFlo allows | Contact sanda support, who can see the request |
| AroFlo asked sanda to slow down, or has switched its API off for every site for now | Nothing to do: the run waits, then carries on |
| AroFlo allows one stream per site | The site already has a stream. Add what you want to read to that one |
| An AroFlo stream runs at most every hour | AroFlo's allowance is per day, so the schedule can't run more often. Choose every hour or slower |
| AroFlo refused those API keys, or didn't accept them, when you connect | Check each value against AroFlo's page and type them again. AroFlo says when the API user has no API access, which a site administrator turns on in AroFlo |
| That site is already connected | Type the keys again on the site that is listed, rather than connecting it twice |
| AroFlo's daily allowance for this site is spent until midnight in Melbourne, when you connect | AroFlo can't say which site the keys are for until then, so nothing was kept. Connect it after midnight in Melbourne |

A failed run counts toward the stream pausing itself, as any stream's does. A run that stopped for the allowance didn't fail. See [runs](https://docs.sanda-os.com.au/streams/runs).

## Limits

- A stream runs at most once every 60 minutes, because AroFlo allows each site 2,000 requests a day. A run stops once fewer than 500 are left.
- A run makes at most 0.9 requests a second, below AroFlo's own limits, which the site's other integrations share. Each request asks for up to 500 records, or 100 for tasks, invoices and bills, which come with their lines or totals.
- **Days read again** is a whole number of days, up to 90.
- One run lands at most 20,000,000 records, so a long history is several runs.

An AroFlo stream is billed like any pull: a charge for each run that does its work, and the rows it lands past what your edition includes. See [what streams cost](https://docs.sanda-os.com.au/streams#what-streams-cost).

:::links
- [Pull](https://docs.sanda-os.com.au/streams/in): Every way records come into your warehouse on a stream.
- [Stream graphs](https://docs.sanda-os.com.au/streams/graphs): Model what a stream lands, then send the result out, every time it lands.
- [Runs](https://docs.sanda-os.com.au/streams/runs): What each run did, and what to do when one fails.
:::
