Skip to content

Program creation and editing — frontend integration

This guide covers the v2 company and superadmin program creation flows, general program editing, and program auction-settings editing. It includes configurable Dichit hosting, required branch ownership, and the validation rules for externally hosted auctions.

The auction contracts are development-only: clients must explicitly send a hosting choice when creating an auction-based program. There is no compatibility fallback that treats a missing choice as Dichit hosting.

1. Hosting and form visibility

bid controls winner selection; isAuctionHostedOnDichit controls where the auction runs. These are separate choices.

Bid type Hosting choice Dichit-specific sections
LOT Omit hosting or send false; true is rejected Hide
AUCTION Explicit true or false required at creation Show only for true
AUCTION_AND_LOT Explicit true or false required at creation Show only for true

For externally hosted auctions, hide these sections and omit their keys from both create and edit requests:

Section Request fields
Has Prebid and dependent options hasPrebid, prebidMutabilityPolicy, prebidDocumentRequired, prebidDocumentTemplateVersionId
Auction automation triggers autoStart, approvalRequired, autoOpenPrebid, autoRevealPrebids, autoDeclareWinner
Advanced auction details auctionBidMode, auctionClosingMode, auctionDurationSeconds, auctionExtensionSeconds, auctionFirstCallSeconds, auctionSecondCallSeconds, auctionThirdCallSeconds, subscriberJoinGraceSeconds, auctionJoiningRule, allowStaffOfflineBids
Optional Dichit preset selector auctionPresetId

Do not send null, false, 0, or empty strings as substitutes for hidden fields. Omission is sufficient. Supplied fields still undergo normal type, range, dependency, and ownership validation; valid supplied settings are stored as dormant configuration, not rejected simply because hosting is external.

On edits, omitted values are retained. Turning hosting off does not erase the saved prebid settings, triggers, preset, or advanced configuration. They can be used again if hosting is enabled later. Creating a fresh program without these fields leaves its existing defaults in place (hasPrebid: false, prebidDocumentRequired: false, and unset optional auction overrides).

hasFirstCycleAuction is not a Dichit-only option: it describes whether the first cycle is auction-eligible. Keep it in the creation payload where required below, including external hosting. General program fields, money fields, and allowedSecurities are not removed by the hosting choice.

Recommended UI state:

type Bid = 'LOT' | 'AUCTION' | 'AUCTION_AND_LOT';
type HostingChoice = 'DICHIT' | 'EXTERNAL' | null;

function showDichitSections(bid: Bid, hosting: HostingChoice): boolean {
  return bid !== 'LOT' && hosting === 'DICHIT';
}

function hostingPayload(
  bid: Bid,
  hosting: HostingChoice
): {
  isAuctionHostedOnDichit: boolean;
} {
  if (bid === 'LOT') return { isAuctionHostedOnDichit: false };
  if (hosting === null) throw new Error('Select where the auction is hosted.');
  return { isAuctionHostedOnDichit: hosting === 'DICHIT' };
}

Keep “not selected” distinct from false. Do not use a truthiness check to validate the hosting choice. Validate only visible sections in the form and build requests from an allowlist of fields, not the entire form state.

2. Endpoints, authorization, and responses

Send Authorization: Bearer <access-token> and JSON bodies. Company routes use the authenticated company; do not send companyId in those bodies. Admin routes require SUPERADMIN; admin creation step 1 requires the target companyId. Both company and admin creation step 1 require a branchId returned by the branch API. The branch must belong to the target company, be active, have an operational address, and not be archived/deleted. All path IDs and template/preset/branch IDs must be valid CUID2 strings returned by the API.

Operation Company Superadmin
Create new, step 1 POST /v2/companies/programs/new/steps/1 POST /v2/admin/programs/new/steps/1
Create new, step 2 POST /v2/companies/programs/new/:programId/steps/2 POST /v2/admin/programs/new/:programId/steps/2
Import existing, step 1 POST /v2/companies/programs/existing/steps/1 POST /v2/admin/programs/existing/steps/1
Import existing, steps 2–5 POST /v2/companies/programs/existing/:programId/steps/:step POST /v2/admin/programs/existing/:programId/steps/:step
Edit general program fields PATCH /v2/companies/me/programs/:programId PATCH /v2/admin/programs/:programId
Read/edit auction settings GET/PATCH /v2/companies/programs/:programId/settings GET/PATCH /v2/admin/programs/:programId/settings
Transfer eligible draft POST /v2/companies/programs/:programId/transfer Not exposed

Creation steps 1 and 2 and general PATCH return HTTP 200 with data.programId. Existing-program step 1 also returns mayBeCompletedCycles. The common response envelope is { status, code, message, data, error }. For example, a successful step 2 response has this shape:

{
  "status": "success",
  "code": 200,
  "message": "Additional details have been added.",
  "data": { "programId": "tz4a98xxat96iws9zmbrgj3a" },
  "error": null
}

Messages can differ by endpoint; do not branch on the success message. Creation step 2 requires a non-deleted INCOMPLETE program with approval status UNDER_REVIEW; the existing-program flow additionally requires preStarted. New-program step 2 submits the company program (APPLIED), or drafts an admin-created program (DRAFTED), so subsequent edits use PATCH, not step 2. Existing programs remain incomplete through steps 2–4 and finalize at step 5.

3. Shared value conventions

Value Contract
Money/commission Send decimal strings, e.g. "100000", "5.5". Do not send currency symbols, separators, or empty strings. See the decimal validation caveat in section 11.
Booleans JSON true / false, not strings. Only fields explicitly marked nullable accept null.
Dates Send consistent ISO date strings, preferably YYYY-MM-DD for date-only controls. The backend parses dates and applies calendar-day bounds. Handle backend date errors even if local validation passes.
frequency MONTHLY, BI_MONTHLY, QUARTERLY, WEEKLY, BI_WEEKLY
durationType DAYS, WEEKS, MONTHS, YEARS
bid LOT, AUCTION, AUCTION_AND_LOT
Counts Send JSON numbers. Use integers for slots, divisions, durations, and completed cycles; some routes currently validate only numeric minima.
Optional fields Omit untouched fields. Do not rely on unknown fields being stored or on a GET object being a valid PATCH body.

4. Creation step 1: all fields and cross-field validation

Every field listed as present in a flow is required unless stated otherwise. Both company and admin flows require branchId; admin also adds companyId.

Field New program Existing-program import
branchId Required CUID2 Required CUID2
name String, length ≥ 1 Same
chitId String, length ≥ 1 Same
totalAmount Decimal, declared minimum 0 Same
subscriptionAmount Decimal, declared minimum 0 Same
minimumBidAmount Decimal, declared minimum 0 Same
frequency Supported enum Same
totalSlots Number ≥ 0 Integer ≥ 0
availableSlots Number ≥ 0 Not part of this step
durationValue Number ≥ 1 Integer ≥ 1
durationType Supported enum Same
divisions Number ≥ 1 Integer ≥ 1
startDate Today or later Today or earlier
lastDateOfSubmission Today or later Not part of this step
lastAuctionDate Not part of this step Today or earlier
companyId Admin only, required CUID2 Admin only, required CUID2

Cross-field checks:

  • branchId must identify a program-ready branch in the selected company.
  • Company employees need program-creation access within the selected branch.
  • Both flows: subscriptionAmount <= totalAmount and minimumBidAmount <= totalAmount.
  • New: availableSlots <= totalSlots and lastDateOfSubmission <= startDate.
  • Existing: subscriptionAmount <= minimumBidAmount and startDate <= lastAuctionDate.
  • New creation does not impose the existing flow's lower bound on minimumBidAmount relative to the subscription. General editing does check that lower bound when either participating money field changes.

Trim user-entered names and identifiers in the UI; the program string validators check length but do not themselves reject whitespace-only values.

5. Creation step 2: all fields and conditional requirements

5.1 General and prebid fields

Field Validation and requirement
foremanCommission Required decimal; declared range 0–100. New-program validators explicitly enforce this range.
delayFineAmount Required decimal; declared minimum 0. Existing-program step 2 also enforces delayFineAmount <= subscriptionAmount.
bid Required enum.
isAuctionHostedOnDichit Required boolean for both auction bid types. For LOT, omit or send false; true fails. Never nullable.
description Required string, length ≥ 1.
hasFirstCycleAuction Required boolean for new company/admin programs and admin existing-program step 2. Not accepted by company existing-program step 2. Must be false for LOT. See section 11 for the admin-import limitation.
completedCycles Existing programs only: required integer ≥ 1, no greater than the server-calculated possible cycles from the program's start date and frequency.
acceptedTermsAndConditions Company step 2 only: required literal true. Omit for admin.
hasPrebid Optional for explicitly external auction programs. Otherwise required boolean; for LOT, send false. Not nullable here.
prebidDocumentRequired Optional for explicitly external auction programs. Otherwise required boolean; for LOT, send false. Not nullable here.
prebidMutabilityPolicy IMMUTABLE_CANCEL_ONLY or MUTABLE_UNTIL_CLOSE. Optional in new programs. Required in existing-program step 2 unless hosting is explicitly external, including the current existing-LOT contract. Not nullable here.
prebidDocumentTemplateVersionId Optional CUID2, not nullable at creation. Must satisfy template ownership/status checks below.
auctionPresetId Optional CUID2 or null; must refer to a global preset or a preset owned by the program's company.
allowedSecurities Optional array of strings, each 1–50 characters. Codes must exist and be visible to the program's company. Empty array is accepted. Not nullable.

Prebid and selection dependencies still apply to supplied fields:

  • LOT rejects hasPrebid: true, hasFirstCycleAuction: true, prebidDocumentRequired: true, a prebid template ID, or a non-null auction preset ID.
  • A required prebid document or a supplied template ID requires hasPrebid: true in the creation request. Omitted hasPrebid does not imply enabled prebid.
  • With hasPrebid: false, autoOpenPrebid: true and autoRevealPrebids: true are rejected.
  • A template version must belong to an active company-owned PREBID template for this company and be PUBLISHED. A missing/foreign/wrong-purpose template returns 404; an inactive/unpublished template returns 400.
  • prebidDocumentRequired: true does not itself require an explicit template version ID; the system can resolve its default template.
  • Omitted advanced settings and automation flags are allowed for Dichit-hosted programs too; their effective values come from the configuration layers.

5.2 Automation and advanced-field validation

All fields in this table are optional in creation and general PATCH. For external hosting, omit the entire group. “Nullable” in this table applies to creation/general PATCH; settings PATCH has additional nullable fields (section 8).

Field Accepted value Nullable
autoStart Boolean Yes
approvalRequired Boolean Yes
autoOpenPrebid Boolean; cannot be true with explicitly disabled prebid Yes
autoRevealPrebids Boolean; cannot be true with explicitly disabled prebid Yes
autoDeclareWinner Boolean Yes
auctionBidMode DISCOUNT_INCREASING, TOTAL_VALUE_DECREASING No
auctionClosingMode DURATION_MODE, CALL_MODE No
auctionDurationSeconds Integer ≥ 1 Yes
auctionExtensionSeconds Integer ≥ 0; 0 disables extension Yes
auctionFirstCallSeconds Integer ≥ 0 Yes
auctionSecondCallSeconds Integer ≥ 0 Yes
auctionThirdCallSeconds Integer ≥ 0 Yes
subscriberJoinGraceSeconds Integer ≥ 0 Yes
auctionJoiningRule BEFORE_START, GRACE_PERIOD, ANYTIME No
allowStaffOfflineBids Boolean Yes

There is no separate autoExtension request key. Use auctionExtensionSeconds. All timing fields are seconds. Convert them to milliseconds only when constructing client-side timers.

5.3 Minimal external payloads

Company new-program step 2:

{
  "foremanCommission": "5",
  "delayFineAmount": "50",
  "bid": "AUCTION",
  "isAuctionHostedOnDichit": false,
  "hasFirstCycleAuction": false,
  "description": "Auction conducted outside Dichit",
  "acceptedTermsAndConditions": true
}

Company existing-program step 2:

{
  "foremanCommission": "5",
  "delayFineAmount": "50",
  "bid": "AUCTION_AND_LOT",
  "isAuctionHostedOnDichit": false,
  "completedCycles": 1,
  "description": "Existing program with externally conducted auctions",
  "acceptedTermsAndConditions": true
}

For admin new creation, remove acceptedTermsAndConditions. For admin existing creation, remove it and add hasFirstCycleAuction: false.

For Dichit-hosted creation, send isAuctionHostedOnDichit: true, hasPrebid, and prebidDocumentRequired; existing creation also requires prebidMutabilityPolicy. Add the optional advanced/automation fields only when the user chooses explicit overrides.

For new LOT creation, use bid: "LOT", hasFirstCycleAuction: false, hasPrebid: false, and prebidDocumentRequired: false; omit hosting or send false. Existing LOT creation additionally requires its policy enum and completedCycles under the current contract.

6. Existing-program creation steps 3–5

These import historical participation, payments, and winners. They remain required by the existing workflow regardless of auction hosting. Company and admin routes use the same body validators. Each requires the target program to be non-deleted, preStarted, INCOMPLETE, and UNDER_REVIEW, with company ownership checked on company routes.

Step Body Validation
3: Subscribers { subscribers: [{ name, mobileNumber, countryCode?, email? }] } Required array, maximum 250 entries; no array minimum in this validator.
4: Payments { cycles: [{ cycleId, subscribers: [{ subscriberId, enrolledSubscriberId, isPaid }] }] } Required arrays; IDs are CUID2; isPaid is a required boolean. No array minimum/maximum here.
5: Winners { cycles: [{ cycleId, winners: [{ division, amount, subscriberId, enrolledSubscriberId }] }] } Required arrays; CUID2 IDs; division is an integer ≥ 1; amount is a decimal with declared minimum 0. No array minimum/maximum here.

Subscriber fields:

  • name: required; trimmed, Unicode-normalized, 1–100 characters; at least one letter; letters, combining marks, spaces, hyphens/dashes, apostrophes and periods allowed; no digits, repeated spaces, or repeated basic punctuation.
  • mobileNumber: required string; normalized; resulting value must be + followed by 10–15 digits. Prefer sending normalized international numbers.
  • countryCode: optional uppercase ISO alpha-2 enum; defaults to IN.
  • email: optional valid email or null; defaults to null.

Repeated mobile numbers are handled by the import flow to support multiple enrollments; do not assume every repeated mobile is rejected. Step 3 creates the historical cycles based on the stored completed-cycle count. Use IDs returned by the existing-program detail endpoints for subsequent requests; client-provided ID syntax alone is not proof of membership. Do not infer winner count, division uniqueness, or amount ceilings from the array validators.

GET .../existing/:programId/steps/4 and /steps/5 provide the selection data. Their query contract supports pagination (default true), pageNumber (integer ≥ 1, default 1), and pageSize (integer 1–100, default 10). Step 5 finishes the import: company programs become APPLIED, admin programs become DRAFTED.

7. General program editing: complete PATCH validation

Load the basic form with GET /v2/companies/me/programs/:programId or GET /v2/admin/programs/:programId, and load the matching settings GET for hosting and resolved auction configuration. Wait for both before initializing hosting-dependent controls; a loading or missing value is not an external hosting selection. Keep the last fetched state separate from the editable form so PATCH contains only intentional changes.

Use the general PATCH endpoints for bid type, basic information, dates, financial fields, first-cycle eligibility, and preset assignment. All fields are optional; at least one recognized field must remain after validation. Omitted fields retain their stored values, subject to the explicit LOT and prebid-off normalization below.

7.1 Field rules

Field Company PATCH Admin PATCH
name String, length ≥ 1 String, 1–255 characters
chitId String, no minimum length String, 1–100 characters
description String, length ≥ 1 String, 1–2000 characters
totalAmount, subscriptionAmount, minimumBidAmount, delayFineAmount Decimal, declared minimum 0 Same
foremanCommission Decimal, declared 0–100 Same
durationValue, divisions Number ≥ 1 Same
durationType, frequency, bid Respective enum Same
totalSlots, availableSlots Number ≥ 1 Number ≥ 0
completedCycles Number ≥ 1 Number ≥ 0
startDate Valid date; business constraints below Same
lastDateOfSubmission Today or later Same
lastAuctionDate Today or earlier Same
images Array of nonempty strings Not accepted by this admin validator
hasFirstCycleAuction, hasPrebid, prebidDocumentRequired, isAuctionHostedOnDichit Boolean, not nullable Same
prebidMutabilityPolicy Policy enum, not nullable Same
prebidDocumentTemplateVersionId, auctionPresetId CUID2 or null Same
allowedSecurities Array of 1–50-character strings; visible existing security codes Same
Automation and advanced fields Section 5.2 Section 5.2

7.2 Cross-field and state restrictions

  • Company generic PATCH rejects an APPROVED program. Admin generic PATCH does not have that approval-status restriction. Use settings PATCH for permitted ongoing auction-setting/hosting changes.
  • Company ownership and non-deleted existence are checked before mutation; another company's program is treated as not found.
  • Validate changes against the merged stored state plus the PATCH, not only pairs of fields present in the request.
  • When relevant financial fields change: subscriptionAmount <= totalAmount, subscriptionAmount <= minimumBidAmount <= totalAmount, and delayFineAmount <= subscriptionAmount.
  • Changed startDate: existing (preStarted) programs cannot use a future date; non-prestarted programs cannot use a past date.
  • For non-prestarted programs, changed slots must maintain availableSlots <= totalSlots; changed submission/start dates must maintain lastDateOfSubmission <= startDate. Admin also checks the slot relationship when both slot fields are supplied. See section 11 for zero-value caveats.
  • Existing-program start/last-auction date editing has a pre-existing reversed comparison; see section 11 rather than copying the creation rule blindly.
  • Supplied template/preset/security IDs follow the ownership and existence rules in section 5. General PATCH checks supplied prebid-only triggers against the patched hasPrebid or, when omitted, the stored program value.
  • If effective prebid is disabled, generic PATCH clears the required-document flag and template pin. To merely choose external hosting, omit hasPrebid and the other dormant settings; do not send hasPrebid: false as a side effect.

7.3 Hosting/bid transitions

Transition Required payload / effect
Existing auction program, unchanged hosting Omit hosting and all unchanged settings.
Dichit → external Send { "isAuctionHostedOnDichit": false }; omit Dichit settings to preserve them. Active-auction blocking still applies.
External → Dichit Send { "isAuctionHostedOnDichit": true }. Existing/default settings are reused; PATCH does not require resending creation fields. Review them in the UI before enabling.
LOT → AUCTION or AUCTION_AND_LOT Send bid and an explicit hosting boolean. No prebid/advanced details are required for the external choice.
Auction → LOT Send { "bid": "LOT" }. Backend sets hosting, first-cycle auction, prebid and required document to false; clears template and preset. Do not send hosting true.
AUCTION ↔ AUCTION_AND_LOT Hosting remains unchanged when omitted.

Changing to LOT has different cleanup semantics from keeping an auction-based bid type and switching its hosting to external.

Example general edit that needs no auction sections:

{
  "description": "Auction managed externally",
  "bid": "AUCTION",
  "isAuctionHostedOnDichit": false
}

8. Program settings GET/PATCH

Use settings GET as the authoritative source for the current hosting boolean. It is returned at data.isAuctionHostedOnDichit, not inside settings or triggerPolicy. Do not assume the general program GET serializer exposes it.

Settings GET and successful settings PATCH return:

  • programId, programName, isAuctionHostedOnDichit.
  • presetId (or null), prebidDocumentRequired, prebidDocument (or null).
  • settings: effective prebid and advanced auction configuration.
  • triggerPolicy: effective values for the five automation flags.
  • allowedSecurities: string array.
  • provenance: supplying layer for the effective configuration fields.

These settings are still returned when hosting is external; they are dormant, not evidence that a Dichit auction is active. Hosting itself is an explicit program choice, not an inherited/provenance field.

Settings PATCH is flat, not { settings: {...} }. All recognized fields are optional; at least one is required. It accepts the section 5.2 fields plus hasPrebid, prebidMutabilityPolicy, allowedSecurities, prebidDocumentRequired, prebidDocumentTemplateVersionId, and isAuctionHostedOnDichit. It does not accept bid, hasFirstCycleAuction, basic program fields, or auctionPresetId; use general PATCH for those.

Differences from creation/general PATCH:

Fields Settings PATCH semantics
hasPrebid, prebidMutabilityPolicy, auctionBidMode, auctionClosingMode, auctionJoiningRule Nullable in addition to their normal types; null removes the program override.
Numeric advanced settings, allowStaffOfflineBids, automation flags Nullable; null removes the program override. Numeric bounds still apply when set.
isAuctionHostedOnDichit, prebidDocumentRequired Optional non-nullable booleans.
prebidDocumentTemplateVersionId Optional CUID2 or null; null removes the pin so default-template resolution can apply.
allowedSecurities Optional array, not nullable; use an empty array to remove the explicit whitelist.

Settings PATCH validates same-patch contradictions: hasPrebid: false with autoOpenPrebid: true or autoRevealPrebids: true fails. It does not impose creation's requirement to send prebid/document fields, and does not require a new hosting choice when the flag is unchanged. It rejects hosting true for a stored LOT program; bid changes belong to general PATCH.

Minimal hosting edit, usable on either company's or admin's settings endpoint:

{ "isAuctionHostedOnDichit": false }

Do not serialize resolved GET settings back wholesale: doing so pins inherited values as program overrides. Send only intentional changes. On external hosting, omit the hidden groups even if they remain populated in form memory.

For layer precedence, presets, provenance, and complete settings response details, see Auction settings and policy.

9. Hosting changes and existing cycles

Disabling hosting is rejected with HTTP 400 while a Dichit auction is in any of these states: PENDING, SCHEDULED, READY, LIVE, PAUSED. ENDED, COMPLETE, and CANCELLED are terminal for this check and do not block it.

A hosting change can assign the nearest eligible unassigned cycle, preferring ACTIVE over UPCOMING. Ended cycles, cycles with a declared winner, and first-cycle-ineligible cycles are not candidates. An eligible cycle is assigned DICHIT with one auction or EXTERNAL without a Dichit auction. Assigned cycles are immutable: changing the program does not convert an already-external cycle to Dichit or rewrite existing Dichit history.

The client must not create a second auction to compensate for a hosting change, assume every cycle changed host, or treat program hosting as a historical per-cycle value. Refresh settings, cycle details, and auction queries after a successful change. The internal auctionHost field is not a new guaranteed public cycle-response field.

10. Errors and frontend acceptance checklist

Handle errors through the existing API envelope; show field validation details when available and a form-level message for lifecycle/ownership/state errors.

HTTP status Relevant cases UI response
400 Missing creation fields, missing auction hosting choice, invalid values, contradictory prebid flags, active auction blocks disabling, approved company general edit Retain form state; show validation/server message; do not mark saved.
401 / 403 Authentication or role failure Follow the application's existing authentication/access handling.
404 Missing/deleted/out-of-company program, inaccessible preset/template, creation program not in the required state Stop mutation; refresh/navigate as appropriate without assuming the record belongs to this company.

Suggested copy for blocked disabling: “This program has an active Dichit auction. Finish or cancel it before switching to external hosting.” Do not automatically cancel auctions as part of a form save.

Frontend checks:

  • New and existing creation work for both auction bid types with external hosting and no prebid/automation/advanced fields.
  • The hosting question cannot be left unanswered for auction-based creation; explicit external (false) is accepted.
  • Switching to Dichit displays the sections and enforces creation's required prebid fields without making optional overrides mandatory.
  • External edit submits only changed general fields/hosting. Hidden values do not become null or false; re-enabling restores saved settings.
  • LOT creation and LOT transitions follow their own rules rather than the external-auction rules.
  • Company/admin and new/existing differences above are reflected in the form.
  • Failed saves do not leave optimistic hosting state committed. Refetch on successful save; a timeout must not trigger ad hoc auction creation.
  • Client-side numeric/date validation and backend errors are both handled.

11. Existing validation caveats (not changed by hosting support)

These are observed implementation differences, not new product requirements:

  1. The shared decimalSchema helper currently calls .refine() without keeping the returned schemas. Its declared min/max bounds are therefore not reliably enforced by that helper. New-program step 2 explicitly checks commission 0–100; cross-field money comparisons are separately enforced as described above. Enforce nonnegative amounts and commission 0–100 in the UI, but do not interpret acceptance of an out-of-range value as supported behavior.
  2. Existing-program creation enforces startDate <= lastAuctionDate, but the general-edit helper currently rejects startDate < lastAuctionDate when one of those dates changes. This is an existing backend inconsistency. Surface the server error; do not silently reverse user dates to satisfy it.
  3. Admin existing-program step 2 requires hasFirstCycleAuction, but the shared existing-program step-2 writer does not persist that field. Keep sending the required boolean for that route; do not present it as a newly supported historical first-cycle override.
  4. New-program creation and generic PATCH use number().min(...) rather than integer validation for several count fields. Some stored-state slot checks also use truthiness, so zero-value admin edits are not uniformly checked. The UI should use integer counts and always enforce available ≤ total.
  5. The route date schemas capture their today-bound when initialized. Around day changes or differing client/server time zones, server validation can differ from the browser's local today. Preserve and show backend errors.

This change intentionally does not alter these unrelated validation behaviors. The field tables describe the expected form constraints; the caveats distinguish them from enforcement gaps in the current backend.

12. Implementation reference

The behavior is covered by the creation-hosting validator matrix, company/admin creation and edit API tests, creation command tests, and program-settings API tests. Relevant source files (repository-relative paths):

  • src/interfaces/http/routes/v2/companies/programs/new/create-new-validators.ts
  • src/interfaces/http/routes/v2/companies/programs/existing/create-existing-validator.ts
  • src/interfaces/http/routes/v2/admin/programs/new/create-new-validators.ts
  • src/interfaces/http/routes/v2/admin/programs/existing/create-existing-validators.ts
  • src/interfaces/http/routes/v2/companies/me/programs/[programId]/program-id-validators.ts
  • src/interfaces/http/routes/v2/admin/programs/[programId]/program-id-validators.ts
  • src/interfaces/http/routes/common/program-auction-settings/program-auction-settings-validators.ts
  • src/application/use-cases/programs/v2/commands/update-program/update-program-by-company-command-helper.ts

No new database migration is required for making the Dichit-only creation fields optional on external hosting. Deploy this contract before the frontend begins omitting those formerly required fields.