API
Better Auth Ruby exposes the same endpoint handlers through Rack and through a server-side Ruby API object.
require "better_auth"
auth = BetterAuth.auth(
base_url: "http://localhost:3000/api/auth",
secret: ENV.fetch("BETTER_AUTH_SECRET"),
email_and_password: {enabled: true}
)Calling API Endpoints On The Server
Every endpoint is available on auth.api with a Ruby snake_case method name.
result = auth.api.sign_in_email(
body: {
email: "john@example.com",
password: "password123"
}
)
session_token = result[:token]Plugin endpoints are exposed the same way after the plugin is configured.
auth = BetterAuth.auth(
secret: ENV.fetch("BETTER_AUTH_SECRET"),
plugins: [BetterAuth::Plugins.magic_link]
)
auth.api.sign_in_magic_link(
body: {
email: "john@example.com",
callbackURL: "/dashboard"
}
)Body, Headers, Query
Use body: for request bodies, query: for URL query parameters, params: for path parameters, and headers: for request headers.
auth.api.send_verification_email(
body: {
email: "john@example.com",
callbackURL: "/dashboard"
}
)
auth.api.verify_email(
query: {
token: params[:token],
callbackURL: "/dashboard"
}
)
auth.api.get_session(
headers: {
"cookie" => request.get_header("HTTP_COOKIE").to_s
}
)Request bodies accept the upstream camelCase keys and the Ruby snake_case aliases where the route supports both.
auth.api.change_password(
headers: {"cookie" => cookie_header},
body: {
current_password: "old-password",
new_password: "new-password",
revoke_other_sessions: true
}
)Getting headers And Rack Response Object
Getting headers
Use return_headers: true when you want the endpoint payload and response headers.
result = auth.api.sign_in_email(
body: {
email: "john@example.com",
password: "password123"
},
return_headers: true
)
cookie = result[:headers].fetch("set-cookie")Getting Response Object
Use as_response: true when you need the raw Rack tuple.
status, headers, body = auth.api.sign_in_email(
body: {
email: "john@example.com",
password: "password123"
},
as_response: true
)
json = JSON.parse(body.join)This is useful for tests, controller integration, and flows that need to forward Set-Cookie headers.
Error Handling
Server-side calls raise BetterAuth::APIError for endpoint errors.
begin
auth.api.sign_in_email(
body: {
email: "john@example.com",
password: "wrong-password"
}
)
rescue BetterAuth::APIError => error
Rails.logger.info(
status: error.status_code,
code: error.code,
message: error.message
)
endFor Rack calls, Better Auth serializes errors as JSON responses.
status, headers, body = auth.call(env)
if status == 401
payload = JSON.parse(body.join)
warn payload.fetch("message", payload["error"])
endRate limit responses use status 429 and include x-retry-after.
status, headers, = auth.call(env)
if status == 429
retry_after = headers["x-retry-after"].to_i
end