Trial & shared-pool number model
A trial number is a lease from a shared platform pool, not a purchase. You claim one free number, use it for a fixed 24 hours, and then either pay to keep it or let it return to the pool for the next organization. The API operations (claim, read, purchase, release) are documented endpoint-by-endpoint in Number lifecycle; this page is the concept-level model behind them β the caps, the timers, and the guarantees. The status vocabulary for purchased numbers lives in Number status map.The lifetime claim: one free trial per organization, ever
Every organization gets exactly one free trial claim in its lifetime. The enforcement is aCOUNT(*) over all of the organizationβs pool assignments β
active, expired, or released β with no rolling time window
(apps/api/src/routes/numbers/number-pool.service.ts:40-48). Once the count
reaches one, a further claim returns 429:
GET /numbers/trial
carries a meta.ever_claimed flag: true once the organizationβs one claim
has been spent, whether or not an assignment is currently active. Clients use
it to stop offering the βClaim a trial numberβ action instead of discovering
the 429 at click time.
Claiming also presumes you own nothing yet: an organization that already holds
purchased numbers (any status other than released) is pointed at a regular
purchase instead β the free trial exists for accounts with no numbers of their
own.
The 24-hour lease and the expiry scheduler
A claim stamps the assignment with anexpiresAt exactly 24 hours after
claim (TRIAL_DURATION_HOURS = 24 in
apps/api/src/routes/numbers/number-pool.service.ts:34). The lease is fixed:
there is no extension, renewal, or longer paid hold. GET /numbers/trial
returns the assignment only while it is unexpired; past expiresAt it reads
null even before cleanup runs.
The actual reclamation is asynchronous. A scheduler sweeps every 30 minutes
(POOL_NUMBER_RELEASE_POLL_MS in apps/webhook-worker/src/worker.ts:1467),
running releaseExpiredPoolNumbers
(apps/webhook-worker/src/scheduler-pool-numbers.ts). Each pass:
- Marks every assignment whose
expiresAthas passed as released. - Returns the number to the shared pool and clears the previous owner, so the next claimant sees clean, unowned inventory.
- Removes the prior organizationβs inbound routing for the number, so no configuration carries to the next claimant.
The daily send cap: 50 messages per day
While the lease is active, sends routed through the trial number are capped at 50 outbound messages per day (TRIAL_DAILY_SEND_LIMIT = 50 in
apps/api/src/routes/numbers/number-pool.service.ts:48) β a spam-control
ceiling on free inventory. The counter spans the trailing 24 hours of outbound
messages from that number and resets as the window rolls. Reaching the cap
rejects further sends from the trial until the window drains; inbound traffic
is unaffected.
Purchase to keep, or release back
Two endpoints decide the numberβs fate:POST /numbers/purchase-trialis the only way to keep a trial number past expiry. It promotes the same digits into your owned inventory as anactivenumber, ends the pool assignment, and β when the number carries a monthly rental β enrolls it in the recurring-billing cycle. It is also the one rate-limited write path shared with claim: claim and purchase are limited to 5 requests per minute per tenant.DELETE /numbers/release-trialends the lease early and returns the number to the shared pool immediately, rather than waiting for the expiry sweep.
The one-hour inbound grace period
A recipientβs reply can arrive moments after you released the number or the lease expired. To keep that reply from falling into a routeless void, inbound resolution first looks for an active assignment and then falls back to any assignment released within the last hour (INBOUND_GRACE_PERIOD_MS in
apps/api/src/routes/numbers/number-pool.service.ts:51). Late-arriving replies
in that window still resolve to you, the prior owner. Once the hour closes β
or a new claimant takes the number β inbound routes to the current owner, and
the released-lease history stops matching.
Worked flow: claim, send, keep or lose
- Claim.
POST /numbers/claim-trialβ accepted while your lifetime count is zero and you hold no purchased numbers (claim/purchase together are rate-limited to 5 requests/min per tenant). - Use. Send up to 50 messages a day, receive inbound, exercise your
routing.
GET /numbers/trialreports the assignment and itsexpiresAt. - Decide. Purchase before hour 24 to keep the digits β after purchase the daily cap no longer applies. Miss the window and the next scheduler sweep returns the number to the pool; additional claims then 429 against the lifetime rule.
What this is not
- Not the aging/reuse flow. The 30/45/90-day parking window, reclaim, and carrier cooldown in Number reuse, aging, and post-release posture govern purchased DIDs you release. A trial number never enters that pipeline: it goes straight back to the shared pool on release or expiry, and the 24-hour lease has no parking grace. One hour of inbound grace is the entire buffer.
- Not a port. Porting moves a number between carriers. Trial DIDs are platform-owned inventory leased to you; the port-out model (Port-out lifecycle and ownership) applies only once you have purchased the number into your inventory.
- Not a subscription. A purchased trial number becomes ordinary owned inventory; from then on the standard billing and renewal model applies.
Tenant-visible guarantees
- Exactly one free claim, enforced permanently. The lifetime count spans every assignment your organization ever held; there is no reset.
- Fixed expiry with bound lag. The lease lasts 24 hours, and the
30-minute sweep reclaims promptly β your
expiresAtis a hard horizon, not a suggestion. - A capped free channel. At most 50 outbound sends per rolling day from a trial number; inbound is uncapped.
- Only purchase preserves. There is no renew or extend path; releasing or expiring always returns the digits to the pool.
- Late inbound is buffered. One hour after release, replies from the lease window still resolve to you rather than erroring or leaking to the next claimant.
- No lease state follows the next claimant. Expiry and release both clear ownership and remove your inbound routing before the number is claimable again.
See also
- Number lifecycle β the four trial operations, request by request
- Number status map β the six-state vocabulary purchased numbers move through
- Number reuse, aging, and post-release posture β the parking/aging model for purchased DIDs
- Port-out lifecycle and ownership model β moving a purchased number to another carrier
- Billing and numbers renewal model β the rental cycle a kept trial number joins