This endpoint answers a bare 404 — "This service does not exist" — for a wrong
billing account, a wrong service name, a queue filed under the other product, and
a number nobody has configured yet. Four different jobs, one message, and nothing
on the Riposte screen could tell them apart. Two rounds went to guessing which one
it was.
The probe now walks there one step at a time and reports what it saw at each: the
billing accounts these credentials can reach, the services each product holds, the
queues in the chosen one. A failure names the step and lists the values that do
exist, which is what somebody can act on.
It also answers its own VERSION. When a test fails identically twice, the first
question is whether the new code is the code that ran, and nothing on the screen
could answer it.
And a failed diagnosis now exits non-zero with an "error" key. Returning ok:false
on exit 0 would have been worse than the 404 it replaces: the screen would call
the instance healthy while naming the step where it is not.
Verified end to end through the real entry point for six shapes — success under
either product, unreachable account, wrong account, number that is not a queue,
service with no queue, and a listing the IAM policy refuses.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TN1R4EnNFUV2Xwtd9q98tj
A queue reached 404 "This service does not exist" on an account where the number
was plainly configured as one. The number was right: the PRODUCT was wrong. OVH
files a call queue under easyHunting or under ovhPabx depending on the offer the
number carries, the hunting routes underneath are identical either way, and the
control panel says "File d'appels" for both — so there is nothing an operator can
look at to tell which one they have.
It is now discovered once per run rather than assumed, and a service found under
neither product says which names DO exist under each. That turns the least
helpful error this API produces — one that reads as a wrong number — into a list
somebody can copy the right value out of.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TN1R4EnNFUV2Xwtd9q98tj
An instance created for on-call routing has no business reading the account, and a
service account narrowed to /telephony — which is exactly how it should be
narrowed — answers 403 "User not granted for this request" on
account:apiovh:me/get. The credentials were perfect and the Test button said they
were not, which is the worst thing a test can do: it sends somebody off to
regenerate a secret that was never wrong.
With telephony coordinates configured the probe now reads the service's call
queues instead. That is worth more than /me ever was: one call proves the
credentials, the IAM policy, the billing account, the service AND the queue. With
no coordinates it still reads the account, which is the right probe for an
instance used to look at servers.
A service found holding no queue is reported as a SUCCESS with a sentence saying
so. It is not a credential failure, and confusing the two is the difference
between "go and create a queue" and an evening spent on authentication.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TN1R4EnNFUV2Xwtd9q98tj
The control panel does not offer application keys on every account — some can only
create a SERVICE ACCOUNT, which authenticates with OAuth2 client credentials. An
instance built that way could not talk to the API at all, which is a hard stop for
anybody whose account is in that shape.
Both schemes are now supported, and which one is used is decided by WHAT WAS
FILLED IN rather than by a mode selector: a client_id and a client_secret mean the
service account, anything else falls back to the signed scheme. One less field to
get wrong, and every existing instance keeps working untouched.
The API base is corrected to /v1 for a service account. The same routes answer
under /1.0 and /v1, but a Bearer token sent to the legacy spelling fails in a way
that reads as "bad credentials" — which sends somebody off to regenerate a
perfectly good secret. An operator should not have to know that the spelling and
the credential go together.
Nothing in config_schema is required any more. The schema cannot express "one set
or the other", and keeping the historical three required would refuse every
service-account instance; the scripts refuse an instance with neither and name
both sets, which is an error somebody can act on — where a form that will not
submit is one they can only fight.
Two things the shared block now caches for the life of the process, both of which
matter on the ladder path that runs every time the rota turns: the access token
(a token request in front of each of a dozen calls would triple the traffic) and
the clock offset against OVH's time (it was a round trip per signed request).
Verified offline for both schemes: the base URL each one talks to, the headers
that land on the wire, the single token request, and the refusal when neither set
is present.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TN1R4EnNFUV2Xwtd9q98tj
OVH API v1, 5 commands: get account, list cloud projects, list/get dedicated
servers. SHA-1 signed application-key auth, stdlib-only. py_compile clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>