Support plans

Four tiers, ranks 0 to 3, and one padlock on one screen.

Support is sold as four PLAN_OBJ records in config.json carrying category "support" and rank 0 through 3: Starter at 0, Developer at 1, Business at 2, Enterprise at 3. They are separate records from the seven membership records, and all four carry a Stripe price_id, including the free Starter rung, which is why support shows up as its own line item on the one consolidated subscription beside membership and ai_workspace. What the rungs change in running code is much smaller than the table implies. Exactly one of the 1010 entries in cpi_methods carries a min_support_plan: submit_support_ticket, which maps ticket to starter, chat to business and call to enterprise. That is the entire ladder as far as the platform is concerned. The server side plan gate, isValidPlan in cpi/http_module/index.mjs, reads only cpi_methods[method].min_plan and compares it against account.membership_plan; it never reads min_support_plan and never reads support_plan. Enforcement is therefore a client side padlock: the dashboard compares two ranks in a flat PLAN_OBJ and greys out a Button. This page reads the four records against the code that actually consumes them, follows one submitted ticket to the places it lands, and ends with a plain list of what the ladder is advertised to do and does not do.

Four records, category support
$0, $100, $1,500, $4,500 a month
One gated method out of 1010
Four tiers, ranks 0 to 3, and one padlock on one screen.
The four records

The ladder as it is written, field by field.

Every cell below is the literal value on the PLAN_OBJ record, not a summary of it. Two things are worth reading across rather than down. The price_note row advertises a percentage surcharge on all three paid rungs, and no file in cpi, dashboard, shared or website-nuxt reads price_note: nothing computes a percentage and nothing charges one. And the Support Agents row carries the identical string on Developer and on Business, word for word, so the middle of the ladder describes the same staff twice at a fifteen times price difference. Monitoring is the literal string "N/A" on three rungs; only Enterprise carries prose.

Starter
Developer
Business
Enterprise
Price per month$0$100$1,500$4,500
price_note (read by nothing)none$100/mo + 2% of monthly Xuda usage$1,500/mo + 5% of monthly Xuda usage$4,500/mo + 7% of monthly Xuda usage
Response Time, first figure<72 hr<4 hr<2 hr<30 min
Production system downnot stated<1 hr<30 min<10 min
Support Channels string / channels arrayEmail / ticketEmail / ticketEmail, Chat / ticket, chatEmail, Chat, Call / ticket, chat, call
Support AgentsCustomer service team offering broad-based assistance.Highly skilled technical staff, proficient in all products.Highly skilled technical staff, proficient in all products. The identical string to Developer.Dedicated technical team, experts in all products.
MonitoringN/AN/AN/AIncident Detection and Response: round the clock proactive surveillance and incident handling for chosen projects.
rank0123
414

Third level topic paths, none of them stored as a document

1 of 1010

cpi_methods entries carrying a min_support_plan

72 hr to 30 min

Full spread of the first response target across four rungs

1

Account id in support_team_account_ids, notified on every platform ticket

Where a request lands

One method, three channels, and only two of them write anything down.

submit_support_ticket is the single method the support ladder gates. It refuses any communication_type outside call, ticket and chat with code -1, then delegates to ticket_submit, which owns tenancy, topics, actions, SLA and notifications. The three branches below are what each channel actually reaches. Two of them create a ticket document on the platform desk. The middle one, the thing Business buys, never calls this method at all.

The floors reach the browser through cpi/app_module/index.mjs, which publishes min_support_plan inside get_metrics, and SupportForm.vue passes it to a Button prop. Those two files are the only consumers of min_support_plan in the repo. Whichever branch writes a document, the third level topic the user picked is not stored as a field: it is appended to the ticket body as a line reading "Detail: <value>". An unknown topic is not rejected either, so the ticket lands untriaged rather than bouncing.
What a rung buys

Four records, one declaration, and a chain that ends at the browser.

Support is its own ladder in PLAN_OBJ, not a field on your membership. Four records carry category "support" and rank 0 to 3: Starter 0, Developer 1, Business 2, Enterprise 3, separate from the seven membership records (free, pro, team, agency and enterprise_t1, t2 and t3). What a rung changes is narrower than the table suggests, and the bullets below say how narrow: one method out of 1010 declares a support floor, and the comparison that enforces it runs in the browser, on a Button.

  • The ladder is priced at 0, 100, 1500 and 4500 a month, and all four rungs carry a Stripe price_id, including the free Starter rung. That is why support appears as its own line item on the single consolidated subscription, beside membership and ai_workspace.
  • The floor is declared once. Exactly one of the 1010 entries in cpi_methods carries min_support_plan: submit_support_ticket, mapping ticket to starter, chat to business and call to enterprise, at clearance user and private true. Nothing else carries a support floor, so reading tickets, replying, reopening, attaching files and closing sit outside the ladder.
  • What visibly changes is three strings. features["Response Time"] reads "<72 hr", then "<4 hr, Production system down: <1 hr", then "<2 hr, Production system down: <30 min", then "<30 min, Production system down: <10 min". Support Channels reads Email, Email, "Email, Chat", "Email, Chat, Call". Monitoring is the literal string "N/A" on three rungs, and only Enterprise carries prose, describing incident detection and response.
  • Two fields on those records are decoration. Each rung carries a channels array (ticket, then ticket and chat, then ticket, chat and call) that no file in cpi, dashboard, shared or website-nuxt reads: the live channel map lives in cpi_methods instead. The paid rungs carry a price_note advertising "$100/mo + 2% of monthly Xuda usage", 5% on Business and 7% on Enterprise, and a repo-wide grep returns config and nothing else. Nothing computes or charges a percentage.
Where the rung is read
  1. config.json declares it
    min_support_plan on submit_support_ticket, the only entry of 1010 that has one
  2. get_metrics publishes it
    app_module copies the floor to the client beside min_plan, even for private methods whose description and fields are withheld
  3. The Button compares ranks
    isFeatureUnlocked reads USER_DATA.support_plan, isValidPlan compares rank in the flat PLAN_OBJ, and Chat or Call renders with a padlock
  4. The server hop that is not there
    http_module isValidPlan reads only min_plan against membership_plan, and ticket_module never reads support_plan at all
The written document

What a submitted ticket actually looks like.

One document lands in xuda_tickets per submission. Three fields are worth reading closely. first_response_due_ts is the only deadline the platform sets, and it is set only for platform tickets, since a customer desk has no support plan to read. resolve_due_ts sits in the same block and nothing ever writes it. And the two fields that would make a ticket move on its own, action_id and assignee, are both null the moment it is created.

xuda_tickets, docType "ticket", filed by a Developer rung account
ticket_no: "XD-000042" is_platform: true source: "platform_support"
channel: "ticket" status: "open" status_stage: "new" priority: 3
topic_name: "Deploy" subtopic_name: "Build Failures" topic_id: "deploy"
details: "...\n\nDetail: Timeout During Build"
the third level is not stored anywhere, it is appended to the body
"sla": {
"first_response_due_ts": now + 14400000, 4 hr, from the plan string
"first_response_ts": null,
"resolve_due_ts": null, no code path ever sets this
"breached": false only the auto close sweep sets it
},
action_id: null resolved from the topic, and the topic carries none
assignee: null nothing auto assigns, on any rung
due_ts: null auto_close: { rule_id: null, warned_ts: null, eligible_ts: null }
The 4 hours in that due timestamp is the FIRST number and unit found in the Response Time string. _tk_response_ms matches one figure and stops, so "Production system down: <1 hr" is never parsed into a deadline, and the clock it starts is plain elapsed time, not the business hours the settings block defines.
On submit

Three things happen when you press Submit.

submit_support_ticket is a thin wrapper. It refuses any communication_type outside call, ticket and chat with code -1, folds the tertiary pick into the body, and hands everything to ticket_submit, which owns tenancy, topics, actions, the SLA and the notifications. What follows is that function, in order.

  1. 01

    The desk, the contact and the number

    Seven of the ten declared ticket sources are platform sources, and a platform source always resolves the tenant to support_team_account_ids[0] whatever the caller passes, so a customer cannot file onto the Xuda desk under their own tenant or the reverse. The requester is matched to a contact under that tenant profile, and a reference is minted from a per tenant counter document: XD-000001 on the platform desk, TK on a customer desk.

  2. 02

    The document and the first interaction

    The ticket is written to xuda_tickets, then the submission itself is logged as interaction one, direction in, from requester, visibility public, carrying your text and any attachments. The topic is matched by id or by name against the seeded ticket_topic documents, and an unknown topic is not rejected: the ticket lands untriaged rather than being lost.

  3. 03

    Two notifications, and no assignment

    The requester gets ticket_opened_customer, which renders the target as Math.round(response_ms / 3600000) hours, so an Enterprise ticket with a 30 minute target is acknowledged with the string "1 hours". Then one ticket_opened_agent notice goes out per uid in support_team_account_ids, and that array holds exactly one id, identical to auto_reply_account_id. Nothing else fires: assignee stays null, and the action pass is skipped because there is no action_id to fire.

The taxonomy

Ten topics, 146 subtopics, and 414 strings that are never stored.

config.support holds exactly three keys: auto_reply_account_id, support_team_account_ids and ticket_topics. The third is three levels deep, and only two of those levels become documents: _tk_seed_platform_topics writes one ticket_topic document per topic with the subtopics inline on it, and the third level is left where it was. The ten topics are grouped below by what they are about, with what each contributes and where a pick lands on the finished ticket.

The seed runs once, when the platform desk has no topic documents yet, copying the config tree as it stands. The 414 third level strings never become data: the one you pick is appended to the ticket body as a line reading "Detail: <value>", searchable as text and queryable as nothing.
Honest limits

Nine things the support ladder does not do, including the one it is sold on.

  • There is no server-side enforcement of it anywhere. The plan gate is isValidPlan in cpi/http_module/index.mjs lines 542 to 547, called at line 868, and it reads exactly one field, cpi_methods[method].min_plan, compared against account.membership_plan. It never reads min_support_plan and never reads support_plan, and ticket_module never reads a support plan for authorisation. The channel lock is a padlock drawn on a Button, and there is nothing behind it.
  • It gates one method out of 1010. submit_support_ticket is the only entry in cpi_methods carrying min_support_plan. Reading your tickets, replying to one, reopening, attaching a file, closing: none of them carry a support floor. The ladder decides which of three buttons is not greyed out on one screen, and stops there.
  • min_support_plan has two readers in the whole repository, and neither is a check. app_module publishes it to the client inside get_metrics, and SupportForm.vue hands it to a Button prop. Nothing in ticket_module consults it, so a Starter account posting communication_type "call" is accepted: the method refuses only a value outside call, ticket and chat, with code -1, and then delegates.
  • PLAN_OBJ also carries a channels array on each support record, ticket, then ticket and chat, then ticket and chat and call. No file in cpi, dashboard, shared or website-nuxt reads PLAN_OBJ.channels. The live channel map lives in cpi_methods instead, which is the one place the two could disagree without anything noticing.
  • "Limited selection" and "Open selection" describe a topic restriction that does not exist. Both are display strings in PLAN_OBJ features.support, and their only occurrences outside config.json are the public pricing table and two explore composables. SupportForm.vue renders every key of _CONF.support_ticket_topics with no plan filter of any kind, so all 10 topics and all 146 subtopics appear identically on free and on enterprise_t3.
  • Chat, the thing Business buys, leaves the platform. The panel gated at Business is a hardcoded tawk.to iframe with no submit button, so submitRequest never runs for it. No ticket document, no ticket_no, no interaction, no transcript in Tickets and no SLA is created for a chat conversation. Of the three channels, only ticket and call write anything.
  • Topic routing does not route. _tk_seed_platform_topics writes all 10 platform topics and their inline subtopics with action_id: null, _tk_resolve_action_id then returns null when neither level carries one, assignee is null at creation, nothing auto-assigns, and every platform ticket notifies the same single account id. Only two of the three topic levels ever become documents: the 414 third-level strings are never stored, and the picked one is appended to the body as a line reading "Detail: <value>".
  • The SLA is one number and it is the softer one. _tk_response_ms takes the first number and unit out of the Response Time string, so the production-down half of every paid rung is never parsed into a deadline. The stored target is plain elapsed time, weekends included, and the acknowledgement email prints Math.round(response_ms / 3600000) hours, which is how Enterprise, whose target is 30 minutes, gets acknowledged with the string "1 hours". And sla.breached is set only by ticket_auto_close_sweep, whose query returns tickets idle for more than 24 hours, so a 30 minute, 2 hour or 4 hour target can never be flagged inside its own window. Auto-close itself ships disabled by default.
  • Around the edges: the price_note advertising "$100/mo + 2% of monthly Xuda usage", 5 percent on Business and 7 percent on Enterprise, is read by no file in cpi, dashboard, shared or website-nuxt, and nothing computes or charges a percentage. Support carries no member_price_id, so the 10 percent member discount does not reach it. No public page sells the ladder. And the Popular articles panel queries a document type nothing in this repository writes.
Before you buy a rung

The questions a support page usually leaves out

Six answers read out of config.json, http_module, app_module and ticket_module rather than off the plan card.

Walkthrough

The walkthrough, and one rule for watching it.

Where the recording and this page disagree about a number, a channel or a response time, the page is the one built from source. Every figure above was read in August 2026 out of the repository copy of config.json and out of cpi/http_module, cpi/app_module and cpi/ticket_module, not carried forward from earlier wording. That rule matters more here than on a page about a screen, because most of what this page reports is an absence: a plan gate that is never evaluated on the server, a chat panel that writes no document, seeded topics whose action_id is null, a breach flag that cannot fire inside its own window. A recording cannot show an absence. It can show the screen where the padlock is drawn, which is the part of the ladder that does work.

File one ticket, then read the record it wrote.

The first exit is the support screen in the dashboard. Pick a topic, a subtopic and the third-level detail, fill the form and submit. That writes a ticket document with a reference beginning XD, a first response target if your rung carries one, an acknowledgement to you and one notice to the single desk account, and apart from the call form it is the only path on that screen that writes anything at all. Read the row it creates in Tickets: the reference, the target, the null assignee. Everything above is checkable there in about a minute. The second exit is the plan screen, where support is bought as its own category beside membership and AI Workspace and lands as a third line on one consolidated subscription. Go there knowing what the four rungs change: a response string that becomes one elapsed-time deadline, a channel list that draws or removes a padlock in the dashboard, and nothing on the server. The public pricing table carries a single Support level row and does not sell the ladder at all.