Authentication

How to authenticate your API requests

API Key Authentication

This API requires Api Key Authentication on every request. This method ensures strong authentication and prevents request tampering by digitally signing every API request.

⚠️ Important:

  • If a request includes the X-Devengo-Api-Key-Signature header, it will be validated using this method.
  • If the signature is not present, authentication will fall back to JWT authentication.
  • In the future, all requests will require a digital signature. We recommend transitioning as soon as possible.

How It Works

Each request must be signed using a Base64-encoded HMAC-SHA256 signature. The signature is generated using a combination of equest data, metadata, and a secret key.

Step 1: Construct the Data to Sign
To generate the signature, the following elements must be concatenated in this exact order:

  1. Base64-encoded Request Payload (if the request has a body)
  2. Nonce. The unique id you must generate for every request.
  3. Request Timestamp. Unix Timestamp format.
  4. API Key Identifier. Provided by Devengo for every api key generated.

The resulting string will look like this:

Base64(REQUEST_BODY) + NONCE + TIMESTAMP + API_KEY_ID

Step 2: Generate the HMAC-SHA256 Signature
Once the data string is created, compute the HMAC-SHA256 digest using the secret provided in the moment of creation of the API Key as the encryption key:

HMAC-SHA256(secret, data_to_sign)

Then, Base64 encode the resulting signature.

Step 3: Send the Signature in the Request

Include the generated signature and required metadata as HTTP headers:

X-Devengo-Api-Key-Signature: {base64_encoded_signature}
X-Devengo-Api-Key-Nonce: {nonce}
X-Devengo-Api-Key-Timestamp: {timestamp}
X-Devengo-Api-Key-Id: {api_key_id}

Timestamp Validation Windows

The signature is only considered valid if the request's timestamp is within a 60-second window from the current server time. During signature validation, we check that the timestamp provided in the request is not older than 60 seconds. If it falls outside this allowed time drift, the signature is deemed invalid and the request is rejected.

Example Implementations

Ruby (if the request has a body)

require "base64"
require "json"
require "securerandom"
require "uri"
require "net/http"

# API credentials
api_key_id = "your-api-key-id"
api_secret = "your-secret-key"

# Request details
nonce = SecureRandom.uuid
timestamp = Time.now.to_i.to_s
payload = '{ "example_key": "example_value" }'
encoded_payload = Base64.strict_encode64(payload)
data_to_sign = "#{encoded_payload}#{nonce}#{timestamp}#{api_key_id}"

signature = Base64.strict_encode64(OpenSSL::HMAC.digest("SHA256", api_secret, data_to_sign))

host = "https://api.sandbox.devengo.com"
endpoint = "/v1/auth/api_key_signature/test"
uri = URI("#{host}#{endpoint}")

request = Net::HTTP::Post.new(uri.path, {
  "Content-Type" => "application/json",
  "X-Devengo-Api-Key-Signature" => signature,
  "X-Devengo-Api-Key-Nonce" => nonce,
  "X-Devengo-Api-Key-Timestamp" => timestamp.to_s,
  "X-Devengo-Api-Key-Id" => api_key_id,
})

http = Net::HTTP.new(uri.host, uri.port)
request.body = payload
response = http.request(request)
puts "Response: #{response.code} #{response.message}"

Ruby (if the request has NO body)

require "base64"
require "json"
require "securerandom"
require "uri"
require "net/http"

# API credentials
api_key_id = "your-api-key-id"
api_secret = "your-secret-key"

# Request details
nonce = SecureRandom.uuid
timestamp = Time.now.to_i.to_s
data_to_sign = "#{nonce}#{timestamp}#{api_key_id}"

signature = Base64.strict_encode64(OpenSSL::HMAC.digest("SHA256", api_secret, data_to_sign))

host = "https://api.sandbox.devengo.com"
endpoint = "/v1/auth/api_key_signature/test"
uri = URI("#{host}#{endpoint}")

request = Net::HTTP::Post.new(uri.path, {
  "Content-Type" => "application/json",
  "X-Devengo-Api-Key-Signature" => signature,
  "X-Devengo-Api-Key-Nonce" => nonce,
  "X-Devengo-Api-Key-Timestamp" => timestamp.to_s,
  "X-Devengo-Api-Key-Id" => api_key_id,
})

http = Net::HTTP.new(uri.host, uri.port)
response = http.request(request)
puts "Response: #{response.code} #{response.message}"

JSON Web Token Authentication (deprecated)

Although API key signature authentication is the recommended method and will soon become the only available option, JSON Web Token (JWT) authentication is still temporarily supported as a transitional measure.

You'll have to get a token using the authentication endpoint. Once you have it, please attach the token to your following requests using the Authorization header.

Authorization: Bearer {JWT_TOKEN}

All endpoints that require authentication will respond with a 401 HTTP status code and the following body if you don't provide a valid token:

{
  "error": {
    "message": "Unauthenticated",
    "code": "authorization",
    "type": "invalid_request_error"
  }
}