Last updated 2026-09-19

Partners / resellers

OtiumWork supports external resellers that sell on behalf of your company — distinct from your internal sales team and from your customers. They get their own portal with role-gated views, submit deals for approval, see commission accruals, take certifications, and ask technical questions.

To use this feature, an admin must enable the Partner portal module on the company at /admin/modules. New tenants default OFF.

Who can manage resellers

Two kinds of account reach the Partners menu:

  • Tenant admins — always, as part of the admin role.
  • Partner managers — any employee or manager whose Partner access flag is set. Grant it at /admin/employees/<id>/edit → Module access → Grant Partner access. The person keeps their existing role; the flag adds the Partners menu and nothing else.

The flag is deliberately narrow. It opens the reseller surface — resellers, deal intents, questions, training, performance — and no other admin page. A partner manager still cannot reach company settings, billing, employees, modules, or any other /admin screen unless they separately hold the admin role.

What about the money?

Partner economics travel with the Partner access flag: commission %, gross vs NET, payables, and the revenue and commission charts on 📈 Performance are all visible, because a channel manager who can't see a reseller's margin can't do the job.

This is scoped to partner money only. Partner access grants no company-wide finance visibility — no P&L, employee costs, hourly rates, payroll, invoices or expenses. That remains the separate Finance access flag, and the two are independent: you can hold either, both, or neither.

Not to be confused with the reseller-side roles inside the portal itself (principal, salesperson, technical, finance, viewer — see Partner portal). Those govern people at the reseller. Partner access governs your own staff managing them.

Adding a partner

  1. Navigate to Partners → Resellers in the sidebar (admin only).
  2. Click + New partner. Set name, default commission %, territory notes. W-9 status is for US 1099 tracking.
  3. After save, you're on the partner detail page. Add contacts (one row per person at the partner who needs portal access). Each contact gets a role:
Role Sees Can do
principal Everything for this partner Manages other contacts, signs reseller agreement, all of the below
salesperson This partner's deals (no commissions, Performance or reporting — principals only), the folders shared with them Submits deal intents, uploads POs, asks questions, takes certifications
technical No deals and no money — only the folders shared with Technical, plus Training Uploads technical questions, takes certifications
viewer No deals and no money — only the folders shared with them, plus Training Asks questions
sales_marketing No pipeline, commissions, targets or reporting — the sales & marketing folders shared with them, plus Training Ask Otium for sales advice and technical answers, asks questions

Deal visibility is one switch: principal, salesperson and finance have it, technical and viewer do not. A role without it gets no Dashboard, Deals or Performance tab, lands on Resources when it signs in, and is refused a deal page, a PO and even a deal's name in the "ask about" picker. There is no own-deals-only split for a salesperson yet — they see everything their reseller has.

Which Resources folders a role sees is set per folder on the Documents side (Partner visibility → roles), not by the role itself. Training is open to every role; which modules appear is set per module by required-for-role.

  1. Each new contact gets an invite email (link valid 7 days) sent automatically. They click to access the portal — no password needed; sessions last 30 days.

Deal intents

When a partner contact submits a deal via /partners/deals/new, it lands as a quote.kind='partner_intent' row in Partners → Deal intents. Review queue lets you:

  • Approve → flips kind to quote, the row enters your normal quote → invoice flow with partner_id and the chosen commission rate locked in. Partner sees status change in their portal.
  • Reject → stays as partner_intent with status='rejected'. Add a note (visible to partner).

All decisions log to audit_log. No bypass: every deal goes through admin review before it can become a sellable quote.

Since 2026-09-19: - A partner can register an opportunity without an estimate — a blank amount shows as a dash, never "$0", and is left unset in Salesforce. - The decision email leads with the reason (required to reject) and names the portal and the deal in the subject; approvals show in the decision history alongside refusals. - Rehearse the Salesforce push from the intent (Deal intents → Preview push) to see exactly which Account / Contact / Opportunity fields would land where, without writing anything to the org. - Contact first and last name are asked separately so the Salesforce Contact is never created with an empty first name; Lead type is picked from Salesforce's own list. - Won deals: on an approved opportunity the partner records the PO (/partners/deals/<id>/won); the sale is created with the partner's commission rate only — the internal commission rate stays 0 (no commission stacking; enforced by a database trigger). Won deals split into expected (PO recorded) and received (payments logged). If the PO falls through, the partner (or you) marks it fell through: the sale is cancelled with a reason and the opportunity returns to the stage it was at. - Stage and next-step changes made by a partner notify the channel desk; vendor-side stage changes notify the partner.

Deal updates (logged in Salesforce as activities)

Since 2026-10-14. On any approved deal — open or won — a partner principal or salesperson can Log update on the deal's page: what happened (Meeting, Call, Demo, Email, Decision-maker meeting, Proposal review, Other), the date it happened (today or earlier, never in the future), a short note (who was there, how it went), and optionally a new next step with a date. Finance, Technical and Viewer contacts see the updates but can't log them. Updates are never edited or deleted — each one is a dated record.

Each update goes to Salesforce straight away as a completed Task on the opportunity, so it shows in the opportunity's activity history and Salesforce's Last Activity Date moves:

Update Task Subject Task Subtype
Meeting [<Salesperson>] Logged_Meeting Call
Call [<Salesperson>] Logged_Call Call
Demo [<Salesperson>] Logged_Meeting_Demo Call
Email [<Salesperson>] Logged_Email Task
Decision-maker meeting [<Salesperson>] Logged_Meeting_DecisionMaker Call
Proposal review [<Salesperson>] Logged_Meeting_ProposalReview Call
Other [<Salesperson>] Logged_Update Task

<Salesperson> is the reseller's Salesperson value (e.g. Partner - TurboTech). The note becomes the Task's Description, with the next step and who logged it. A Demo also fills First Demonstration Date (only if it's empty); a Decision-maker meeting sets Decision Maker Meeting Date; a Proposal review sets Proposal Review Meeting Date — each only if the org has that field.

  • Each update shows In Salesforce ✓, Pending, Retrying or Not in Salesforce (with the reason). A deal not yet pushed keeps its updates Pending and sends them with its first push.
  • The channel desk is emailed for every update.
  • Admins (partner access) can log an update on a reseller's behalf from Partners → → Open pipeline → Updates →; it's recorded as theirs, for the reseller.
  • Last update shows on every deal in the partner's Deals page and in the admin Open pipeline: the later of the partner's own last update and the opportunity's Last Activity Date in Salesforce (so calls your own reps log there count too). A deal with nothing for 30+ days is marked quiet, and both lists have a Quiet 30+ days filter.
  • Monthly reminder (1st of the month): each reseller's principal and salesperson contacts get one email listing their quiet deals.

Two-way sync with Salesforce on every partner change

Every partner change to an approved deal that is already in Salesforce — logging an update, next step, amount, customer, description, close date, name, stage — first reads that one opportunity back from Salesforce:

  • Gone over there (deleted, or no longer visible): nothing is pushed, the deal is flagged not in Salesforce, the partner is told plainly that the change was saved in Otium only, and the channel desk is emailed once. Nothing is ever deleted automatically.
  • Changed over there in a field the partner did not just change (name, stage, amount, close date, next step, description): Salesforce's value is taken into Otium before the push and audited as updated from Salesforce; the partner sees "Updated from Salesforce: …". The field the partner just changed is the newest change and wins. A stage of PO Received - Closed Won is never taken (only a PO closes a deal here), a name is taken only if it keeps the same deal type (the type sets the commission), and a closed deal's name, stage, amount and close date stay as the PO settled them.
  • Salesforce unreachable: the change is saved in Otium and queued; a retry job runs every 15 minutes and tries again (reading back first). An update gives up after 8 tries and shows Not in Salesforce.

Otium tells "changed over there" apart from "changed here" with a snapshot of what it last saw in, or wrote to, Salesforce for each opportunity.

Salesforce → Otium every 15 minutes

The sync runs in both directions. Otium → Salesforce happens the moment a partner (or you) saves a change. Salesforce → Otium runs on a schedule: every 15 minutes Otium asks Salesforce which reseller opportunities changed since it last looked (scripts/cron_partner_sf_pull.py, read-only) and settles each one:

Changed in Salesforce What happens in Otium
Opportunity deleted The deal is archived — it leaves the partner's pipeline, the portal and the performance counts, but the row is kept, never deleted. The reason says who deleted it and when. Restore it in Salesforce and it comes back by itself on the next run. A deal that already has a PO here is not archived (it's money) — it is flagged for you to look at.
Name, stage, amount, close date, next step, description Taken into Otium, by the same rule as above: only what changed in Salesforce since Otium last looked, and a change made in Otium that hasn't reached Salesforce yet wins. Audited as updated from Salesforce (by salesforce-sync).
Stage Closed Lost The deal closes as lost here too, with the loss reason when Salesforce lets Otium read it; the partner is emailed.
Stage PO Received - Closed Won, no PO in Otium Flagged Marked won in Salesforce — confirm the PO on the reseller's page. Only recording the PO creates the sale and the commission.
Salesperson changed to another reseller The deal moves to that reseller (unless it already has a PO — then it's flagged).
Salesperson changed to anyone else The deal is archived ("Reassigned in Salesforce to …").
New opportunity with an active reseller's Salesperson value Imported as an approved deal for that reseller, as Import from Salesforce would. A same-named deal already here that isn't linked is not duplicated — it's listed for you instead.

Your channel desk gets one email per run listing anything archived, restored, moved, lost, marked won or imported. Plain field updates are audited, not emailed.

Each deal row shows Synced with Salesforce … and the reseller's page shows Last Salesforce sync: …, N changes, with the archived deals in a fold-out list underneath the pipeline. If the sync can't reach Salesforce it changes nothing, retries on the next run, and sends one breakage alert (not one every 15 minutes).

Filters and reporting

Resellers and you work from the same deal table, each limited to what they may see (Valentine, 2026-10-14):

  • Reseller portal → Deals → All deals (/partners/deals?view=table): every deal the reseller has — pending approval, open, won, lost, not approved — in one table. Filter by status, stage, product, customer/deal search, a date range on the registered, close or won date, and quiet 30+ days; sort any column; totals at the top; ⬇ CSV downloads exactly what's shown. Long lists page (50 at a time, or Show all) — nothing is cut off silently any more, on this page or on the Overview.
  • Reseller portal → Performance / Dashboard: a period picker (this quarter, last quarter, year to date, last 12 months, all time, custom) with win rate, average deal size, average days from registration to win, open pipeline by stage, and commission earned in the period (roles that see commissions). The quarter-by-quarter charts are still below.
  • Reseller portal → Commissions: filter by when the deal closed and by whether the customer has paid, with a CSV. Whether we have paid the commission out isn't tracked yet, and the page says so.
  • Partners list: filter by status (active / not active / paused / terminated) and search by name or Salesforce value; columns for open pipeline, won/lost this year and last activity.
  • Partners → All reseller deals (/admin/partners/deals): the same table across every reseller, plus filters for reseller, Salesforce salesperson and reseller status, and a CSV.
  • Partners → Performance: the same period figures, filtered by reseller and reseller status, a per-reseller table for the period, and Direct vs Partner read from Salesforce — open pipeline by stage, won/lost and win rate, and how many open opportunities had activity in the last 30 days, split by the opportunity's Salesperson value (anything starting "Partner" is a reseller) with a per-salesperson breakdown. That comparison is read when you press Read / Refresh from Salesforce and kept for the page, so the page itself never waits on Salesforce.

A reseller can't widen their scope from the address bar: the reseller and salesperson filters exist only on the admin side, and archived deals are never shown in the portal.

Ask Otium for resellers

The reseller portal has an Ask Otium tab — the first partner-facing AI (Valentine, 2026-10-14). Switch it on or off for the whole workspace at Partners → Ask Otium log (on by default).

  • Technical questions (every portal role) are answered directly from the AxSTREAM wiki, through the same wiki connection Sales AI uses, with the wiki pages linked. Otium only re-words what the wiki returned. If the wiki doesn't confirm something, the answer says so instead of guessing, and the reseller can press Send to the channel team — that opens a normal question in your Questions inbox with the question and the answer attached.
  • Sales advice (principal and salesperson) follows the Champion Playbook's method — discovery, champions, objections, next steps — in neutral words. The playbook stays private: it is never quoted, listed or summarised, no SoftInWay employee is named and no personal stories are retold, and an answer that copies the playbook is replaced (the same backstop Valentine Voice uses for the internal team). The Sales AI knowledge base (internal pricing ranges, bucket rules) is deliberately not used for resellers.
  • Guardrails for both: no prices or price ranges — any currency figure in an answer is replaced with a pointer to the price list or the channel team; no discounts, free trials, delivery dates, licence or legal commitments; nothing about other resellers or our own direct deals.
  • Limits and review: each reseller contact can ask 50 questions a day. Every question and answer is in Partners → Ask Otium log (/admin/partners/ask-log), filterable by reseller and kind, where you can flag an answer that was wrong.

Claude connector for resellers

Reseller contacts can connect Claude (claude.ai, desktop or phone) to the portal. Each contact finds the steps under Profile → Connect Claude; the connector URL is /partners/mcp on this site, and they sign in with their own Partner Portal email and password (the same lockout and "choose a password first" rules as the portal).

Claude gets exactly what the contact's portal role allows — the tool list itself is cut to the role:

Tools Who gets them
Ask technical (wiki-only, cited), Resources (list + short-lived download link), Questions (list, ask, follow up) Every role (Ask needs Ask Otium switched on)
Ask sales advice Principal, Salesperson, Sales & marketing
Deals list / one deal, Performance summary Roles that see deals (Principal, Salesperson, Finance)
Register a deal, log an update, change stage / amount / close date / next step Principal, Salesperson
Commissions summary Principal, Finance (Salesperson: own)

Every change Claude makes runs through the same code as the portal button — Salesforce push, two-way sync, channel-desk emails and the audit trail all behave identically (the audit row also records source: mcp). Calls are rate-limited per contact.

Revoking: Partners → the reseller → Claude connections lists who has connected; revoke one connection or all of them. Deactivating a contact or resetting their password revokes their connection automatically. Reseller tokens are separate from staff connector keys — neither works on the other's endpoint.

Q&A inbox

Any partner contact, whatever their role, can submit pre-sales technical questions at /partners/questions/new, optionally tied to an opportunity or a signed deal picked from a list. A question is a conversation: either side can reply (/partners/questions/<id>/reply), every message notifies the other side, and replying reopens a closed thread. Admins respond at Partners → Questions; Close when fully resolved.

Training & certifications

Modules + auto-graded quizzes — annual re-certification supported.

  1. Partners → Training → + New module. Set kind (sales/technical/both), required-for-role, pass threshold (default 80%), valid-for-days (default 365 = annual).
  2. Quiz JSON uses this structure: json {"questions": [ {"q": "What's our standard discount cap?", "choices": ["5%","10%","15%","20%"], "correct_idx": 1}, {"q": "...", "choices": [...], "correct_idx": 0} ]}
  3. Partners see required modules on their dashboard + at /partners/training. They take the quiz; on pass, a certification row is written with expires_at = completed_at + valid_for_days.
  4. Failed attempts don't break anything — partners can re-take immediately.

Document sharing

Partner contacts can see documents in any document_space marked partner-visible:

  1. Edit a document space (under /documents/spaces).
  2. Under Share with resellers pick all resellers or one reseller, and tick the portal roles that may see it (none ticked = everyone at the reseller). Spaces are private to resellers until you do this.
  3. Partners see allowed spaces at /partners/resources and download via the same app/storage.py path internal users use. Same audit log.

The channel team manages Resources. Anyone with Partner access (not only admins) runs Partners → Resources (/admin/partners/resources): upload files into a reseller folder and pick a category for each — Price list, Brochures, Presentations, Case studies, Datasheets, Other — re-tag many files at once, set a folder's default category, and see how many times each file was downloaded or viewed, by which resellers (Who →). The generic Documents upload form has the same category dropdown. A file with no category takes its folder's default, or is guessed from the folder/file name.

Resellers see Resources grouped by category with search, a folder filter, file-type icons and sizes, View for PDFs (opens in the browser) and Download, paging instead of the old silent 50-file cap, a new since your last visit badge, and the price list pinned at the top of Resources and on their dashboard. Different resellers can get different price lists: put each in a folder shared with that one reseller. Once a day (scripts/cron_partner_resources_digest.py) each reseller contact gets one email listing new files they can see — never one per upload.

Who can manage these spaces: admins, and anyone with Partner access. A partner manager can create, rename, re-share and archive spaces that are shared with resellers (and sub-folders under them), but not internal spaces, and not the internal access gates or QMS link — those stay with admins.

Schema reference

  • partner — one row per reseller organization.
  • partner_contact — multiple per partner, with role.
  • partner_session — magic-link sessions, 30-day idle expiry.
  • partner_question — Q&A inbox, optionally deal-linked.
  • training_module — module + quiz JSON + pass threshold + validity.
  • certification — polymorphic: either partner_contact_id OR employee_id set (CHECK constraint). Same framework will be reused for internal-employee certifications in v2.
  • sale.partner_id + sale.partner_commission_rate — partner attribution + override of partner.default_commission_pct.
  • sale.po_document_id — PO uploaded by partner, attached to the sale.
  • quote.kind (quote | partner_intent) + quote.partner_id + quote.partner_intent_* — partner-submitted deal-intent state.
  • document_space.partner_visible_to_id + document_space.partner_role_visibility — role-gated visibility.
  • company.module_partner_portal_enabled — per-tenant feature flag.

Sequencing & limits (v1)

What's NOT implemented yet (revisit when needed): - Cert expiry banners with date math — dashboard shows certs but doesn't compute "expires in N days" client-side. - Multi-currency partner commissions. - Tiered commission structures. - Territory exclusivity / hard collision blocking on deal intents. - Partner-driven sub-resellers. - 1099 threshold automation (we track w9_status only — actual 1099 generation lives in QuickBooks). - API access for partners (no Salesforce-to-OtiumWork sync). - Internal-employee certification (schema is ready; UI not wired).

For migration off Salesforce: enable the partner portal module → add ABC Mexico (or whoever) as a partner → invite their principal contact → they invite their team or you do → done. Salesforce seats can come down once they've used the portal for a real deal cycle.


See something wrong or outdated in this article? Report it →