A tiny Ruby client for the OVH REST API, built on Faraday. It handles OVH's request-signing authentication and gives you a thin, predictable HTTP client — nothing more.
- One method per HTTP verb —
get,head,post,put,patch,delete. - Automatic request signing with the OVH application/consumer keys.
- Consumer-key flow and clock-skew handling built in.
- Typed errors (
OvhRest::ClientError/ServerError) instead of leaking Faraday. - Optional retries on rate limiting and transient server errors.
- Credential redaction in logs.
- Requirements
- Installation
- Quick start
- Making requests
- Obtaining a consumer key
- Configuration
- Clock skew
- Retries
- Logging
- Error handling
- Documentation
- Development
- License
- Ruby >= 3.2 (also tested on JRuby and TruffleRuby).
Add the gem to your Gemfile:
git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" }
gem 'ovh-rest', github: 'jbox-web/ovh-rest', tag: '1.0.0'then run bundle install.
You will need an application_key, an application_secret and a consumer_key.
Create an application at the OVH API console for your region
(EU, CA,
US); see
Obtaining a consumer key to mint the consumer key.
require 'ovh-rest'
ovh = OvhRest.new( # shortcut for OvhRest::Client.new
application_key: '<application_key>',
application_secret: '<application_secret>',
consumer_key: '<consumer_key>',
)
# GET an SMS account status — the parsed JSON body is returned as a Ruby Hash
ovh.get('/sms/sms-xx12345-1')
# => { "status" => "enable", "creditsLeft" => 42, "name" => "sms-xx12345-1", ... }
# POST to send an SMS
ovh.post('/sms/sms-xx12345-1/jobs', {
'charset' => 'UTF-8',
'class' => 'phoneDisplay',
'coding' => '7bit',
'priority' => 'high',
'validityPeriod' => 2880,
'message' => 'Dude! Disk is CRITICAL!',
'receivers' => ['+12345678900', '+12009876543'],
'sender' => '+12424242424',
})
# => { "totalCreditsRemoved" => 2, "ids" => [12345, 12346] }Every call returns the parsed JSON response body, or raises on error (see Error handling).
GET, HEAD and DELETE send their parameters as a query string, while
POST, PUT and PATCH send them as a JSON body:
ovh.get('/me/bill', { 'date' => '2024-01' }) # => GET /1.0/me/bill?date=2024-01
ovh.put('/me', { 'firstname' => 'Ada' }) # => PUT /1.0/me body: {"firstname":"Ada"}You can pass extra request headers as a third argument — for example to use the OVH batch mode. They are merged in but never override the signed authentication headers:
ovh.get('/dedicated/server/ns1,ns2', {}, headers: { 'X-Ovh-Batch' => ',' })For uncommon needs you can also reach the underlying call:
ovh.query(method: 'GET', path: '/me') # same as ovh.get('/me')
ovh.build_headers(method: 'GET', path: '/me') # just the signed X-Ovh-* headersIf you don't have a consumer key yet, start the OVH credential flow. The call is
unsigned and only needs the application key; the response carries a
consumerKey and a validationUrl the end user must visit to activate it:
ovh = OvhRest.new(
application_key: '<application_key>',
application_secret: '<application_secret>',
consumer_key: nil,
)
ovh.request_consumer_key(
[{ 'method' => 'GET', 'path' => '/*' }],
redirection: 'https://example.com/callback', # optional
)
# => {
# "consumerKey" => "...",
# "validationUrl" => "https://...",
# "state" => "pendingValidation"
# }Inspect the credential currently in use (scope, status, expiration) with:
ovh.current_credentialovh = OvhRest.new(
application_key: '<application_key>',
application_secret: '<application_secret>',
consumer_key: '<consumer_key>',
api_url: 'https://eu.api.ovh.com',
api_version: '1.0',
timeout: 30,
open_timeout: 10,
time_delta: 0,
auto_sync_time: false,
retries: 0,
logger: nil,
)| Option | Default | Description |
|---|---|---|
application_key |
— | OVH application key. |
application_secret |
— | OVH application secret (used to sign requests). |
consumer_key |
— | OVH consumer key (may be nil when only calling request_consumer_key). |
api_url |
https://eu.api.ovh.com |
API endpoint (EU/CA/US). |
api_version |
1.0 |
API version. |
timeout |
30 |
Request timeout, in seconds. |
open_timeout |
10 |
Connection-open timeout, in seconds. |
time_delta |
0 |
Offset added to the local clock when signing (see Clock skew). |
auto_sync_time |
false |
Sync the clock against OVH once before the first signed request. |
retries |
0 |
Retry idempotent requests on 429/5xx (see Retries). |
logger |
nil |
A Faraday-compatible logger (see Logging). |
The configured endpoint is readable via ovh.api_url, ovh.api_version and the
combined ovh.api_uri.
OVH rejects requests whose signature timestamp drifts too far from its own clock. On a host with an unreliable clock, sync once against OVH's time endpoint; the computed offset is reused for every subsequent request:
ovh.synchronize_time!Or let the client sync lazily before the first signed request by passing
auto_sync_time: true to the constructor.
When retries is greater than zero, idempotent requests (never POST) are
retried on rate limiting (429) and transient server errors
(500, 502, 503, 504) with exponential backoff, honoring the
Retry-After header:
ovh = OvhRest.new(application_key: '...', application_secret: '...', consumer_key: '...', retries: 3)Pass any Faraday-compatible logger to trace requests and responses. The
credential headers (X-Ovh-Application, X-Ovh-Consumer, X-Ovh-Signature)
are automatically redacted from the output:
require 'logger'
ovh = OvhRest.new(application_key: '...', application_secret: '...', consumer_key: '...', logger: Logger.new($stdout))A 4xx response raises OvhRest::ClientError, a 5xx raises
OvhRest::ServerError (both subclasses of OvhRest::ApiError), and transport
failures (timeouts, connection errors) raise OvhRest::Error. Everything
descends from OvhRest::Error, so you never need to rescue Faraday classes
directly:
begin
ovh.get('/me')
rescue OvhRest::ClientError => e # 4xx
e.message # OVH error message
e.error_code # OVH "errorCode", e.g. "INVALID_CREDENTIAL"
e.http_code # OVH "httpCode", e.g. "403 Forbidden"
e.status # HTTP status, e.g. 403
rescue OvhRest::ServerError => e # 5xx
e.status
rescue OvhRest::Error => e # transport-level failure
e.message
endThe public API is documented with YARD:
bin/yard doc # generate HTML docs into doc/
bin/yard server # browse them at http://localhost:8808After checking out the repo, run bundle install, then:
bin/rspec # run the test suite
bin/rubocop # lint
bin/guard # auto-run specs on file change
bin/console # IRB session with the gem loadedBug reports and pull requests are welcome on GitHub at https://github.com/jbox-web/ovh-rest.
Released under the MIT License.