⚠
Data sync problem: {{ reloginMessage }}
Change over {{ kpiRangeLabel }}
{{ kpiRangeDates }}
1d
30d
{{ kpiRangeLabel }}
{{ k.label }}
{{ k.value }}
{{ k.sub }}
{{ hoursCard.label }}
{{ hoursCard.cumValue }}
{{ hoursCard.cumSub }}
{{ hoursCard.dayValue }}
{{ hoursCard.daySub }}
Demo
Commercial · {{ com.tabLabel }}
{{ com.subText }}
Asking business.hub.xyz…
Hub Business API not configured
Set HUB_BUSINESS_API_KEY in the server .env and restart the api service. The token stays server-side — the org and contributor codes are deliberately not stored here, because they also open the admin view.
Could not reach the commercial program
{{ com.errorText }}
Connected. This tab is not built out yet — below is the org summary exactly as the Business API returns it, so nothing here can be mistaken for domestic data.
{{ com.raw }}
Loading analytics…
{{ k.label }}
{{ k.value }}
{{ k.sub }}
Acquisition & activation funnel
{{ f.label }}
{{ f.value }}
Onboarding timing — median days (p90 · n)
{{ d.label }}
{{ d.value }}
{{ d.sub }}
{{ t.title }}
| {{ t.dimLabel }} | Applicants | On Hub | Activation % | Avg uploads | Median strap→1st (d) |
|---|---|---|---|---|---|
| {{ r.value }} | {{ r.applicants }} | {{ r.onHub }} | {{ r.activation }} | {{ r.avgUploads }} | {{ r.medianAct }} |
Hub engagement
Uploads distribution
{{ b.label }}
{{ b.count }}
{{ c.label }}
{{ c.value }}
Payments & data quality
{{ p.label }}
{{ p.value }}
Data quality
{{ q.label }}
{{ q.value }}
Loading hours…
{{ k.label }}
{{ k.value }}
{{ k.sub }}
{{ k.label }}
{{ k.value }}
{{ k.sub }}
Hours collected · per day {{ hrs.dayAvgText }}
qualified not qualified pending
{{ d.weekLabel }}
{{ d.label }}
{{ d.value }}
{{ d.qText }} ·{{ d.nText }} ·{{ d.pText }}
{{ hrs.accrueNote }}
Hours collected · per week {{ hrs.weekAvgText }}
qualified not qualified pending
{{ w.label }}
{{ w.value }}
{{ w.qText }} ·{{ w.nText }} ·{{ w.pText }}
{{ hrs.accrueNote }}
{{ t.title }}
| {{ t.dimLabel }} | Hours | Collectors | Avg hrs/collector | Avg length | Uploads |
|---|---|---|---|---|---|
| {{ r.value }} | {{ r.hours }} | {{ r.collectors }}{{ r.collCaret }} | {{ r.avg }} | {{ r.avgLen }} | {{ r.uploads }} |
|
{{ m.hours }}
|
|||||
Top collectors
{{ c.rank }}
{{ c.country }}
{{ c.hours }}
Collectors by hours collected
{{ b.label }}
{{ b.count }}
🎯 Collection goals
{{ hrs.goals.totalText }}
collected so far · {{ hrs.goals.nextLabel }}
{{ hrs.goals.pctText }}
{{ hrs.goals.remainingText }}
{{ hrs.goals.etaText }}
{{ g.emoji }}
{{ g.label }}
{{ g.state }}
{{ g.pctText }}
→
{{ hub.rangeText }}
{{ hub.orgName }} · official partner feed
Hub Partner API not configured
Set HUB_API_KEY in the server .env and restart the api service.
Loading partner data…
!
Couldn't reach the Hub Partner API
The upstream endpoint didn't respond or rejected the request.
{{ k.label }}
{{ k.value }}
{{ k.sub }}
Contributors · payroll view
{{ hub.matchText }}
| Contributor {{ hub.si_email }} | Status {{ hub.si_status }} | Uploads {{ hub.si_uploads }} | Hours {{ hub.si_hours }} | Quality {{ hub.si_quality }} | Diversity {{ hub.si_diversity }} | Last active {{ hub.si_last }} | Airtable {{ hub.si_linked }} |
|---|---|---|---|---|---|---|---|
|
{{ r.title }}
{{ r.sub }}
|
{{ r.status }} | {{ r.uploads }} | {{ r.hours }} | {{ r.quality }} | {{ r.diversity }} | {{ r.lastActive }} | {{ r.linkedLabel }} |
{{ hub.footText }}
Reading account status…
Could not read account status
No status recorded yet — this fills in on the next hourly sync.
{{ k.label }}
{{ k.value }}
{{ k.sub }}
{{ disabled.mixNote }}
Freelancers Hub has switched off
| Person | Handsets off | Hours | Reason | Since | State |
|---|---|---|---|---|---|
|
{{ r.name }}
{{ r.key }}
|
{{ r.devices }} | {{ r.hours }} | {{ r.reason }} | {{ r.since }} | {{ r.state }} |
Disabled is not deleted. These accounts still exist on Hub and keep every hour they recorded — they simply cannot record any more. An account Hub has removed answers 404 and appears on the Deleted accounts tab instead.
"Cannot record at all" is the number that matters. A freelancer on two handsets holds two Hub accounts, so one being switched off still leaves them working; only someone with every handset disabled is actually shut out. Counting accounts instead of people overstates the problem roughly twofold.
Reasons come from Hub. Every one we have seen so far is
"Cannot record at all" is the number that matters. A freelancer on two handsets holds two Hub accounts, so one being switched off still leaves them working; only someone with every handset disabled is actually shut out. Counting accounts instead of people overstates the problem roughly twofold.
Reasons come from Hub. Every one we have seen so far is
minute_app.
Recent status changes
| Account | Change | Hours | When |
|---|---|---|---|
| {{ ch.account }} | {{ ch.move }} | {{ ch.hours }} | {{ ch.at }} |
No history yet. Hub's feed says what is true now, not when it changed, and it is rebuilt every sync — so "since" is only knowable from the moment we started recording it. Changes from here on will be listed, and the Since column fills in as accounts move.
Comparing HR's roster with Hub's accounts…
Could not run the cross-check
HR's sheet could not be read and there is no saved copy to fall back on, so there is nothing to compare Hub against.
{{ crosscheck.sourceNote }}
{{ k.label }}
{{ k.value }}
{{ k.sub }}
{{ p.title }}
{{ p.count }}
{{ p.note }}
{{ p.empty }}
| {{ h }} |
|---|
| {{ c }} |
Why this exists. The Deleted accounts audit can only see an account that reached at least one hourly sync — one created and removed inside that hour would leave nothing behind, not even an id to ask Hub about. HR's sheet is the independent witness: it records who we issued an account to, from our side, without asking Hub anything. Where the two disagree is where to look.
Checking what Hub still has…
Could not load the audit
{{ k.label }}
{{ k.value }}
{{ k.sub }}
Accounts Hub removed
{{ absences.footText }}
No account with recorded hours has gone missing from the Hub feed.
| Account | Hours | Sessions | Active | Hub says |
|---|---|---|---|---|
|
{{ r.email }}
{{ r.hubId }}
our freelancer |
{{ r.hours }} | {{ r.sessions }} | {{ r.seen }} | {{ r.status }} |
How this is established
An account here recorded hours and then stopped appearing in Hub's contributor feed. That alone is not proof — a sync can fail, and Hub has outages — so each one is checked directly: "deleted (Hub 404)" means Hub itself answered
not_found for that account id, which a renamed or merely disabled account would not. A disabled account stays in the feed carrying Status: Disabled; these are absent entirely. Anything not yet confirmed is listed as pending rather than counted as a deletion.
We hold the evidence because our database keeps what Hub's no longer shows: every individual recording session, with Hub's own session id, timestamp and duration, for accounts Hub now says never existed. The export has one row per session.
These hours stay on the invoice — the work was done and approved, and a later deletion on Hub's side does not undo it.
🔒
Bill to Hub is private
This view shows what we invoice Hub. Enter the billing password to continue — it is remembered until you close this browser tab.
!
That password was rejected
The server did not accept it. Try again.
Billing needs the Hub Partner API
Set HUB_API_KEY in the server .env and restart the api service. Hours are read from the official partner feed.
Computing weekly bill…
!
Couldn't compute the bill
The billing endpoint didn't respond.
{{ billing.partialText }}
{{ k.label }}
{{ k.value }}
{{ k.sub }}
Weekly charge to Hub
{{ billing.footText }}
| Week | US hours | LATAM hours | Hours $ | Bonuses | Headbands | Week total | Hub h | Our h | Diff |
|---|---|---|---|---|---|---|---|---|---|
|
{{ r.week }}
{{ r.range }}
hours unavailable — incomplete from our sessions — Hub did not answer |
{{ r.usH }} | {{ r.latamH }} | {{ r.hoursCharge }} | {{ r.bonus }} | {{ r.headband }} | {{ r.total }} | {{ r.hubH }} | {{ r.ourH }} | {{ r.diff }} |
| Total | {{ billing.totalRow.usH }} | {{ billing.totalRow.latamH }} | {{ billing.totalRow.hoursCharge }} | {{ billing.totalRow.bonus }} | {{ billing.totalRow.headband }} | {{ billing.totalRow.total }} | {{ billing.totalRow.hubH }} | {{ billing.totalRow.ourH }} | {{ billing.totalRow.diff }} |
Hub h vs Our h — why they differ
Hub h is Hub's own figure for the week, from the Partner API's
/summary — the number Hub would quote back at us.
Our h is ours: every recording session we have stored for that week, summed from hub_sessions.
They measure the same work through two independent paths, so agreeing to within about 1% is the expected, healthy result — and that is what we normally see.
A small gap is normal and not worth chasing. Ours can run slightly high because a session is counted in the week it started while Hub buckets by its own clock, and slightly low on a week still in progress, because we only hold what the collector has managed to pull so far — the current week almost always closes the gap by the following day.
A gap above 5% is highlighted, and that is worth a look: it usually means the session collector missed accounts rather than that anyone's hours changed.
The bold column is the one the invoice was computed from. Normally that is Hub's per-person feed. When Hub will not answer, the money is computed from our sessions instead and the row says so — same hours, our record rather than Hub's, and worth reconciling before the invoice goes out.
"not read" means Hub has never answered for that week, which is not the same as zero.
Worth checking before invoicing
{{ l }}
Airtable Shipping Requests ↔ Ecommex WMS · syncs every 2h (on the hour, CDMX)
Last sync: {{ deliveries.lastSyncText }}
Deliveries need the Ecommex API
Set ECOMMEX_API_KEY (and ECOMMEX_AIRTABLE_PAT) in the server .env and restart the api + collector services.
Loading delivery KPIs…
!
Couldn't load delivery KPIs
The Ecommex endpoint didn't respond.
Last run failed: {{ deliveries.lastRunError }}
{{ k.label }}
{{ k.value }}
{{ k.sub }}
Sync runs
{{ deliveries.footText }}
| Run (CDMX) | Trigger | Status | Requests | → Ecommex | → Airtable | Duplicates | Flagged |
|---|---|---|---|---|---|---|---|
|
{{ r.when }}
{{ r.message }}
|
{{ r.trigger }} | {{ r.status }} | {{ r.rows }} | {{ r.created }} | {{ r.updated }} | {{ r.dups }} | {{ r.flagged }} |
Deliveries
{{ deliveries.listCountText }}
Nothing matches that search.
| Name | Confirmed | Tracking number | Carrier | Status | Last update | |
|---|---|---|---|---|---|---|
|
{{ r.name }}
|
{{ r.email }} | {{ r.confirmed }} |
|
{{ r.carrier }} | {{ r.status }} | {{ r.date }} |
Computing weekly hours…
!
Couldn't load high achievers
The highachievers endpoint didn't respond.
{{ k.label }}
{{ k.value }}
{{ k.sub }}
Hours by day · target {{ ha.targetText }}
{{ ha.dayFootText }}
| Participant | {{ dh.label }} | Total | Status |
|---|---|---|---|
|
{{ r.name }}
{{ r.sub }}
|
{{ c.text }} |
{{ r.totalText }} | {{ r.badge }} |
Hours by ISO week · target {{ ha.targetText }}
{{ ha.footText }}
| Participant | {{ wh.label }} | Status |
|---|---|---|
|
{{ r.name }}
{{ r.sub }}
|
{{ c.text }} | {{ r.badge }} |
Export range:
→
Loading freelancer hours…
!
Couldn't load freelancers
The freelancers data didn't respond.
{{ k.label }}
{{ k.value }}
{{ k.sub }}
{{ emp.hrWarning }}
Onboarding
{{ k.label }}
{{ k.value }}
{{ k.sub }}
By stage · HR's OB Status
{{ st.label }}
{{ st.value }}
{{ st.sub }}
{{ emp.obTieLine }}
{{ fl }}
{{ emp.panelTitle }}
{{ emp.countText }}
accrues from daily snapshots
{{ emp.weekLabel }}
| # | Freelancer{{ emp.indName }} | HR status | Total{{ emp.indTotal }} | {{ emp.weekColLabel }}{{ emp.indWeek }} | {{ wh.label }} | Last active |
|---|---|---|---|---|---|---|
| {{ r.num }} |
{{ r.name }}
{{ r.email }}
{{ dv }}
|
{{ r.hrStatus }} | {{ r.total }} |
{{ r.thisWeek }}
{{ r.lastWeek }}
|
{{ c.text }} |
{{ r.lastActive }} |
| {{ emp.totalRow.label }} | {{ emp.totalRow.total }} | {{ emp.totalRow.thisWeek }} | {{ c.text }} |
On HR’s list · no Hub account yet
{{ emp.hrMissingCount }} people · closest to recording first · no hours can be tracked until an account exists
| {{ m.name }} |
{{ m.status }} |
Withdrawn by HR · still has an account
can still record hours — offboarding gap
| {{ w.name }} |
{{ w.email }} | {{ w.status }} |
Has an account · not on HR’s list
{{ emp.hrNoRecordCount }} accounts
| {{ n.name }} |
{{ n.email }} |
Crunching weekly segments…
!
Couldn't load the analysis
The overall endpoint didn't respond.
{{ k.label }}
{{ k.value }}
{{ k.sub }}
{{ c.label }}
{{ c.value }}
{{ c.sub }}
Hours per week · click a week to break it down
{{ lg.label }}
| Week | By strategy | Hours | Active | Hrs/person | WoW | |||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
{{ w.caret }}
{{ w.label }}
{{ w.dates }}
|
{{ w.total }} | {{ w.people }} | {{ w.perPerson }} | {{ w.delta }} | ||||||||||||||||||||||||||||||||||||||
|
All week · Mon–Sun
By day · click a day for its strategies
|
||||||||||||||||||||||||||||||||||||||||||
No session weeks to show yet.
{{ ov.footText }}
{{ ovd.ingestNote }}
Loading participants…
{{ ovd.partTitle }}
| {{ h.label }} | |||||||
|---|---|---|---|---|---|---|---|
|
{{ p.name }}
{{ p.sub }}
|
{{ p.hours }} | {{ p.sessions }} | {{ p.avgMin }} |
|
{{ p.peak }}
{{ p.peakSub }}
|
{{ p.lastActive }} | {{ p.status }} |
{{ ovd.partScopeNote }}
{{ ovd.partFoot }}
Loading sessions…
{{ ovd.sessTitle }} · sessions
{{ ovd.sessSub }}
{{ ovd.sessTotal }}
Hour of day · local time
{{ h.label }}
Hour × weekday
{{ ovd.todPeak }}
{{ h.label }}
{{ row.weekday }}
{{ ovd.todFoot }}
Retention by onboarding week
| {{ h }} | ||
|---|---|---|
| {{ c.cohort }} {{ c.start }} | {{ c.size }} | {{ x.text }} |
{{ ovd.cohortFoot }}
Two weeks side by side
vs
{{ ovd.cmp.aTitle }}{{ ovd.cmp.aDates }}
{{ ovd.cmp.bTitle }}{{ ovd.cmp.bDates }}
Change
{{ r.label }}
{{ r.a }}
{{ r.b }}
{{ r.delta }}
By strategy
{{ r.label }}
{{ r.aVal }}{{ r.aSub }}
{{ r.bVal }}{{ r.bSub }}
{{ r.delta }}
By day
{{ r.weekday }}
{{ r.aVal }}{{ r.aSub }}
{{ r.bVal }}{{ r.bSub }}
{{ r.delta }}
{{ ovd.cmp.foot }}
No week data yet.
Loading referrals…
!
Couldn't load referrals
The referrals endpoint didn't respond.
{{ k.label }}
{{ k.value }}
{{ k.sub }}
Referred people · by referrer
{{ ref.footText }}
| Referred | In Hub | {{ wh.label }} | Total |
|---|---|---|---|
| {{ g.label }} {{ g.bizPill }} {{ g.sub }} | {{ c.text }} |
{{ g.totText }} | |
|
{{ r.name }}
{{ r.email }}
|
{{ r.inHub }} | {{ c.text }} |
{{ r.total }} |
| TOTAL | {{ c.text }} |
{{ ref.grand.total }} | |
Loading the call log…
!
Couldn't load the calls
The calls endpoint didn't respond.
{{ cl.warnText }}
{{ k.label }}
{{ k.value }}
{{ k.sub }}
{{ r.name }}
{{ r.count }}
{{ r.sub }}
Calls · newest first
| When | Number | Person | Recruiter | Type | Duration | Status | Rec. |
|---|---|---|---|---|---|---|---|
| {{ r.when }} | {{ r.number }} |
|
{{ r.agent }} | {{ r.dir }} · {{ r.channel }} | {{ r.duration }} | {{ r.status }} |
|
No calls to show.
{{ cl.footText }}