TelgeaDevelopers

Changelog

API changelog and release history.

Unreleased

2026-10-19

  • Analytics: GET /v1/analytics answers 400 BAD_REQUEST to an empty msisdn. It used to ignore the parameter and report usage for every number in scope.
  • Users: GET /v1/users answers 400 BAD_REQUEST to an empty or blank employeeId. It used to ignore the parameter and list every user. If you build either query from a value that can be empty, leave the parameter out instead.
  • User plans: GET /v1/user-plans/search answers 400 BAD_REQUEST to an empty iccid.
  • User plans: POST /v1/user-plans/{userId}/termination now ends a plan whose number is suspended. The number stays suspended until the end date and is deactivated 14 days later, as any ended number is. Before, the request was accepted but the number was never ended. The same holds for a number suspended after its end was booked. Until the end date such a plan reads TERMINATING with terminationScheduledAt set, where it used to read INACTIVE.
  • User plans: a plan whose number is suspended now reads ACTIVE, where it used to read TERMINATING. Suspending a number doesn't end the plan, and the plan is still billed while the number is suspended. Such a plan reads TERMINATING only once its end is booked. If you filter user plans by status, suspended numbers now come back under ACTIVE.

2026-10-16

2026-10-15

  • Data pools: the docs for GET /v1/data-pools/{poolId}/members now say to count usageBytes once per userId. A member with several lines is listed once per line, and each row carries the member's usage across all their lines, so summing the rows counts that member more than once. The response itself has not changed. If you add up usageBytes across rows, deduplicate by userId first.
  • Webhooks: UserPlanCreated and UserPlanActive fire once for each local number a user takes, not only for their first. Both payloads add localNumbers, every local number the user holds, oldest first. UserPlanActive keeps localNumber, which still describes the user's first local number. For a user with one number nothing else changes: localNumbers holds that one number. Each item's sim is null while the number has no eSIM yet.

2026-10-12

  • All endpoints: every name you send or read is camelCase. This renames the snake_case names that were left. On GET /v1/usage, the query is startDate, endDate, pageSize and userId. Each record has timeStarted, timeEnded, servingCountryCode, usageClass, counterpartyMsisdn, callDirection, counterpartyCountryCode, networkType, userId, totalPrice, unitPrice and rateId, and a page has nextCursor and hasMore. On GET /v1/analytics, the query is startDate, endDate, userId, countryIsoCode and orderBy. On GET /v1/rates, it is servingCountry and asOfDate, and each rate has ratePerUnit and validFrom. Every error carries requestId, and its extension members are camelCase too: missingPermission, revokedAt, countryCode, planEndedAt, currentState, requiredStates, executionDate, firstDate and lastDate. The old names are no longer accepted or returned.
  • All endpoints: since 2026-10-06, every error is an RFC 9457 Problem Details body served as application/problem+json. It's a flat object with type, title, status, detail, code and request_id, where it used to be wrapped in error. message is now detail, and hint is gone: type is a URI that opens the error's own page, which says what to do about it. code keeps its values, and fields and missing_permission keep their place, now at the top level. Many refusals that used to answer with a generic code have their own type, such as hub/PORT_IN_ALREADY_IN_PROGRESS. Branch on code or type. See Errors.
  • User plans and port-ins: a market can require your organization to pass business verification before it provisions a user plan or ports a number in there. Until it has, POST /v1/user-plans and POST /v1/port-ins answer 403 COUNTRY_NOT_SUPPORTED with the type ORGANIZATION_VERIFICATION_REQUIRED, and nothing is created. Send the request again once your organization has passed. No market requires verification yet.
  • Port-ins: since 2026-10-05, POST /v1/port-ins/validate needs the port-ins:validate permission instead of port-ins:create. Every key that held port-ins:create was given it, so no existing key needs a change. Grant it in Console to a new key that validates port-ins.
  • Port-ins: the execution date must be one of the dates the market offers, and the 7-day minimum is gone. Each type on GET /v1/port-ins/fields/{countryCode} lists the dates in executionDate.dates, which hold until the market's midnight, executionDate.validUntil. GET /v1/port-ins/fields/{countryCode}/schema carries the same list as $defs.executionDate. How soon the first date is and how far ahead the last differ by market, and weekends are refused in every market. See the port-in guide.
  • Port-ins: POST /v1/port-ins/validate and POST /v1/port-ins refuse a date outside the list with 400 BAD_REQUEST and the type provider-platform/EXECUTION_DATE_NOT_OFFERED, which names the market's first and last dates. The date is checked once every other field passes. Until 2026-10-12 only some markets checked the date, and a refused one answered 200 with valid: false or status: "REJECTED". A malformed executionDate answers VALIDATION_ERROR, and earliest_execution_date is gone with the 7-day minimum.
  • Port-ins: GET /v1/port-ins/fields/{countryCode} has carried texts for guiding your customer through the form since 2026-10-07. They were not announced until now. Each type can carry explanation, to help pick it where a market has more than one, and timing: when the port-in is submitted to the carrier, who sets the final date, whether it can still move, and up to when it can be cancelled or rescheduled. Each field can carry help (what the value is and whose details it is), howToGet (where to find a value such as a PAC) and example (a value in the accepted format). Each text has an id that never changes, to key a translation off, and the English in en. See the port-in guide.
  • Port-ins: each type on GET /v1/port-ins/fields/{countryCode} carries typeLabel in a market with more than one type: the label over the choice of type, such as "Who is the number registered to?", to head the choice beside each type's explanation.
  • Port-ins: since 2026-10-07, each entry in errors from POST /v1/port-ins/validate, and from a REJECTED answer to POST /v1/port-ins, names its field and carries a code to branch on: REQUIRED, FORMAT_INVALID, LENGTH_INVALID, NOT_ALLOWED or DATE_NOT_ALLOWED. Since 2026-10-08 it can also be CHECK_DIGIT_INVALID or WRONG_CUSTOMER_TYPE. New codes will be added; show the message for one you don't recognise. The port-in guide says what each means.
  • Port-ins: since 2026-10-08, validating or creating a port-in catches typos the carrier would otherwise refuse days later. A wrong check digit in a Swedish personnummer or organisationsnummer, a Spanish DNI or NIE, or a French RIO answers CHECK_DIGIT_INVALID. A Swedish organisationsnummer sent with personal answers WRONG_CUSTOMER_TYPE. A French landline RIO and a German date of birth in the future answer NOT_ALLOWED. Values are also accepted as people type them: spaces, separators and lower case in a UK PAC, a French RIO, Spanish and Swedish ID numbers and SIM numbers, and a US ZIP+4. A SIM number in Denmark and Belgium no longer needs at least 18 digits.
  • Port-ins: since 2026-10-05, POST /v1/port-ins/validate and POST /v1/port-ins answer 409 CONFLICT for a number Telgea already holds: one on a Telgea line, or one whose plan ended in the last 14 days and can still be restored. Validating a number with a port-in already in progress answers 409 too, as creating one always has. Neither creates anything.

2026-10-09

  • Port-ins: POST /v1/port-ins/{portInId}/discard discards a port-in that ended in REJECTED or FAILED. The number leaves the plan, which reads INACTIVE unless the user has another number, so you can give the user a new number or port in a different one. A port-in that can't be discarded answers 400 BAD_REQUEST with the type PORT_IN_NOT_DISCARDABLE. It needs the new port-ins:discard permission, which no existing key holds; add it to your key in Console.

2026-10-08

  • Port-ins: failure on GET /v1/port-ins/{portInId} and GET /v1/port-ins/number/{numberId} carries text: what happened and what to do next, in English, for showing to your customer. It includes the market's advice where there is some, such as where to find a SIM number, and is set even when reason is null. Its wording may change at any time; keep branching on reason and field.
  • Webhooks: failureDetails on PortInRejected and PortInFailed carries text too, written when the event is sent. It can leave out market advice that GET /v1/port-ins/{portInId} includes.

2026-10-01

  • Usage: GET /v1/usage returns records in the order we stored or last corrected them, so a partner that resends the cursor of its last page receives late records and corrections it has not seen. Expect usage from the last 24–48 hours to keep changing as late records arrive and are rated.
  • All endpoints: the per-key request quota now resets 24 hours after the window opens, as documented. It previously only reset after a key had been completely idle for 24 hours, so a key called more often than once a day spent its 1000 requests over its lifetime and was then refused with RATE_LIMIT_EXCEEDED until it went quiet for a full day. A key already in that state starts a fresh window on its next request; no change is needed on your side.

2026-09-23

  • User plans: the provisioning guide notes that in a market whose operator publishes no API a change can take a couple of hours rather than minutes. No endpoint behaves differently — poll, and do not read a long PROVISIONING as a failure.

2026-09-21

  • Usage: the usage guide gives the typical delay before each market's usage reaches the feed, and says to re-read from the previous day on every poll. The feed was described as near real time; in Belgium and Spain a record typically arrives 13 to 16 hours after the usage.
  • User plans: the provisioning guide gives the average time to provision a line in each market, from under a minute to 35 minutes in the United States.
  • Port-ins: an execution date in Spain, Sweden or the United Kingdom must be a weekday. This was already enforced, but not documented.

2026-09-18

  • Documentation: the reference is split into the Enterprise API and the Partner API, with one page per resource and each operation naming the permission its key needs. Links to the old one-page-per-operation reference redirect to that operation on its resource page.
  • All endpoints: the Idempotency-Key header is now declared on every endpoint that changes data, so it appears in the reference, the playground and the OpenAPI description. Its behaviour is unchanged since 2026-09-16.
  • User plans: DELETE /v1/user-plans/{userId}/data-restriction lists each restored number once. A customer whose line draws on several data pools used to see the number repeated once per pool.
  • Errors: stopping and restoring data now say what went wrong instead of answering with a generic message: data already stopped (409), nothing to restore (409), no line in one of your data pools (422), and a number that cannot be stopped yet (400). Each hint names the call that fixes it. The usage feed says the same of a cursor it did not issue (400).
  • Usage: a window over 31 days, or ending before it starts, is refused as a VALIDATION_ERROR naming end_date, instead of a generic 400.
  • Errors: replacing a SIM inside the cooldown now says the number's current SIM is too new to replace. The cooldown counts from when that SIM was issued, so it also applies to a line provisioned moments ago.
  • Reference: every endpoint documents the 429 returned once an API key has used its request quota. The quota always applied to every endpoint; only three documented it.

2026-09-17

  • User plans: POST /v1/user-plans/{userId}/termination ends a customer's line on a date you name. The line is suspended on that date and deactivated at the network 14 days later, so a 202 means the termination is booked rather than already carried out. Read it back on GET /v1/user-plans/{userId}, which now carries terminationScheduledAt — the date the plan is scheduled to end, or null when none is. Billing is not prorated: the customer keeps the period they already bought, and activeUntil in the response says when it ends. Send deleteUser: true to delete the end-user with the line; otherwise DELETE /v1/users/{userId} succeeds once the line is suspended, without waiting out the 14 days. The endpoint requires the orders:cancel permission on your key.

2026-09-16

  • Port-ins: a port-in that ended in REJECTED or FAILED now carries a failure object on GET /v1/port-ins/{portInId} and GET /v1/port-ins/number/{numberId}, giving the reason it ended that way and the field the donor objected to, where one was identified. failure.reason is null when the cause could not be classified; treat an unrecognised or absent reason as an unexplained failure rather than an error. New reasons will be added over time.
  • Port-ins: submitting a port-in for a number that already has one in flight now answers 409 CONFLICT with a message naming the conflict, instead of a generic error. This was always the behaviour; it is now a documented response. Retry once the previous attempt has reached a terminal state: where failure.reason and failure.field identify a correctable input, fix it before resubmitting; where the reason is null or one you do not recognise, resubmit as it stands.
  • User plans: POST /v1/user-plans/{userId}/data-restriction stops a customer's mobile data while leaving calls and texts working — for someone who has used up what they paid for — and DELETE on the same path restores it. Both take an optional reason, kept with the change and visible to our support staff. They require numbers:suspend and numbers:unsuspend on your key.
  • User plans: POST /v1/user-plans/{userId}/suspension stops all traffic on a number, domestic and international — for a phone that has been lost or stolen — and DELETE restores it. Name the number with numberId, which you get from GET /v1/user-plans/{userId}. The response reports the numbers the change actually reached at the network. Unlike the data pair these carry no reason: a suspension is a state of the line at the provider with nowhere to record one. Same permissions as above.
  • All endpoints: requests that change state (POST, PATCH, DELETE) now accept an optional Idempotency-Key header. Retrying a request with the same key returns the original response instead of creating a duplicate or failing with a conflict, so a dropped connection no longer needs special handling. A key identifies one request to one endpoint and is remembered for 24 hours, replaying the original outcome including a failure — use a new key once you change the request. Keys are private to your API key and organisation. The header is ignored on GET, and a value outside 1–255 printable ASCII characters is rejected with 400 BAD_REQUEST. Requests that send no key are unaffected.

2026-09-15

  • Data pools: GET /v1/data-pools lists every active pool in your organization with how much of each is left — bought, consumed and remaining, in GB — and whether any number drawing on it is currently restricted. GET /v1/data-pools/{poolId}/members lists the users drawing on one pool with the bytes each has taken, so you can see which customer to throttle before the pool runs out. A pool belonging to another organization answers 404, not 403. Both require the pools:read permission on your key.
  • Rates: New GET /v1/rates returns the pay-as-you-go retail price list for a market — international calling, SMS and MMS per destination, or roaming data per visited country. Pass the home-plan market (ISO 3166-1 alpha-2) and a rate type; each rate carries the country, the per-unit price as a number to six decimal places, its unit, the currency and when it took effect. Prices are your organization's, with any negotiated rate in place of the list price. Calling and messaging are priced from the market by default — add serving_country to price that plan while roaming (market=DE&serving_country=DK), and the response echoes the serving_country it used. ROAMING_DATA prices every visited country at once, so it ignores the parameter and answers with serving_country: null. Calls and messages are priced outbound by default; add direction=inbound to price being reached instead — chargeable while roaming — and the response echoes the direction it used (null for ROAMING_DATA). Prices are those in effect today; add as_of_date (YYYY-MM-DD) to read another day's list, echoed as as_of_date. Every country is listed: one that costs nothing carries rate_per_unit: 0 rather than being left out — whether it is priced free (calls inside the EU/EEA) or has no published pay-as-you-go price, such as a roaming-data country included in the plan. A market no carrier serves answers 403 COUNTRY_NOT_SUPPORTED. Requires the rates:read scope.

2026-09-14

  • Usage: GET /v1/usage returns raw voice, SMS and data records as one chronological feed, so you do not have to know that we store calls and data separately. Page with cursor and next_cursor rather than page numbers — a cursor is stable while new records arrive behind it. page_size is at most 100, the start_date/end_date window at most 31 days, and a key may make 1000 requests per 24 hours. Prices are null until a record is rated and on usage covered by a pool. Requires the consumption:read permission on your key.
  • Analytics: GET /v1/analytics returns the same consumption already totalled per interval (day or month), with the highest, lowest and average interval across the window, so a dashboard does not have to add up the raw feed. Narrow it with user_id, msisdn or country_iso_code. Requires the analytics:read permission on your key.

2026-09-11

  • Users: firstName and lastName must now be non-blank when you create a user, and when you include them while updating one. A blank or whitespace-only name is rejected with a 400 VALIDATION_ERROR that names the field; leading and trailing whitespace on names is trimmed before the user is stored. Requests that already send real names are unaffected.

v1.0.0

Initial public API release.

  • Users: List, create, update, and delete organization users
  • Teams: List and create teams for user grouping
  • User Plans: List, get, and create telecommunications plans

On this page