{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-getting-started/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["tabs","tab","card","cards"]},"type":"markdown"},"seo":{"title":"Overview","description":"Bookable is a TMS API gateway API — one integration to access real-time availability and manage bookings across venues on any table management system.","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"overview","__idx":0},"children":["Overview"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["client_id"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["client_secret"]}," are available in the ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://portal.bookabletech.com"},"children":["Bookable Portal"]}," under your account settings."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To securely access the Bookable API, obtain an OAuth 2.0 access token using the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Client Credentials"]}," flow:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["POST your credentials to the token endpoint."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Cache the returned token along with the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["expires_in"]}," value."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Attach the token to every API request as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Authorization: Bearer <token>"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Re-fetch the token when it is close to expiry (recommended: 60 seconds before)."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"token-endpoint","__idx":1},"children":["Token endpoint"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Environment"},"children":["Environment"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"URL"},"children":["URL"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Production"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://auth.bookabletech.com/oauth/token"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Sandbox"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://auth-sandbox.bookabletech.com/oauth/token"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The examples below include token caching — avoid requesting a new token on every API call."]},{"$$mdtype":"Tag","name":"Tabs","attributes":{"size":"medium"},"children":[{"$$mdtype":"Tag","name":"div","attributes":{"label":"cURL","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl -X POST https://auth.bookabletech.com/oauth/token \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"grant_type\": \"client_credentials\",\n    \"client_id\": \"YOUR_CLIENT_ID\",\n    \"client_secret\": \"YOUR_CLIENT_SECRET\",\n    \"audience\": \"api.bookabletech.com\"\n  }'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Copy the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["access_token"]}," from the response and use it in your API calls:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl https://api.bookabletech.com/venues \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\"\n","lang":"bash"},"children":[]}]},{"$$mdtype":"Tag","name":"div","attributes":{"label":"JavaScript","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"// Node.js — token manager with caching (fetch available natively in Node 18+)\nclass TokenManager {\n  #accessToken = null;\n  #expiresAt = 0;\n\n  async getAccessToken() {\n    if (this.#accessToken && Date.now() < this.#expiresAt - 60_000) {\n      return this.#accessToken;\n    }\n\n    const response = await fetch('https://auth.bookabletech.com/oauth/token', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify({\n        grant_type: 'client_credentials',\n        client_id: process.env.BOOKABLE_CLIENT_ID,\n        client_secret: process.env.BOOKABLE_CLIENT_SECRET,\n        audience: 'api.bookabletech.com',\n      }),\n    });\n\n    if (!response.ok) {\n      throw new Error(`Auth failed: ${response.status} ${await response.text()}`);\n    }\n\n    const data = await response.json();\n    this.#accessToken = data.access_token;\n    this.#expiresAt = Date.now() + data.expires_in * 1000;\n\n    return this.#accessToken;\n  }\n}\n\n// Usage\nconst tokenManager = new TokenManager();\nconst token = await tokenManager.getAccessToken();\n\nconst apiResponse = await fetch('https://api.bookabletech.com/venues', {\n  headers: { Authorization: `Bearer ${token}` },\n});\n","lang":"javascript"},"children":[]}]},{"$$mdtype":"Tag","name":"div","attributes":{"label":"Python","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import time\nimport requests\nimport os\n\nclass TokenManager:\n    def __init__(self):\n        self._access_token = None\n        self._expires_at = 0\n\n    def get_access_token(self) -> str:\n        # Return cached token if still valid (with 60s buffer)\n        if self._access_token and time.time() < self._expires_at - 60:\n            return self._access_token\n\n        response = requests.post(\n            'https://auth.bookabletech.com/oauth/token',\n            json={\n                'grant_type': 'client_credentials',\n                'client_id': os.environ['BOOKABLE_CLIENT_ID'],\n                'client_secret': os.environ['BOOKABLE_CLIENT_SECRET'],\n                'audience': 'api.bookabletech.com',\n            }\n        )\n        response.raise_for_status()\n\n        data = response.json()\n        self._access_token = data['access_token']\n        self._expires_at = time.time() + data['expires_in']\n\n        return self._access_token\n\n\n# Usage\ntoken_manager = TokenManager()\ntoken = token_manager.get_access_token()\n\napi_response = requests.get(\n    'https://api.bookabletech.com/venues',\n    headers={'Authorization': f'Bearer {token}'}\n)\n","lang":"python"},"children":[]}]},{"$$mdtype":"Tag","name":"div","attributes":{"label":"Java","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"java","header":{"controls":{"copy":{}}},"source":"import java.net.http.*;\nimport java.net.URI;\nimport java.time.Instant;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\n\npublic class TokenManager {\n    private static final HttpClient client = HttpClient.newHttpClient();\n    private static final ObjectMapper mapper = new ObjectMapper();\n\n    private static String accessToken;\n    private static Instant expirationTime;\n\n    public static String getAccessToken() throws Exception {\n        if (accessToken != null && Instant.now().isBefore(expirationTime.minusSeconds(60))) {\n            return accessToken;\n        }\n\n        String body = \"\"\"\n            {\n              \"grant_type\": \"client_credentials\",\n              \"client_id\": \"YOUR_CLIENT_ID\",\n              \"client_secret\": \"YOUR_CLIENT_SECRET\",\n              \"audience\": \"api.bookabletech.com\"\n            }\n        \"\"\";\n\n        HttpRequest request = HttpRequest.newBuilder()\n                .uri(URI.create(\"https://auth.bookabletech.com/oauth/token\"))\n                .header(\"Content-Type\", \"application/json\")\n                .POST(HttpRequest.BodyPublishers.ofString(body))\n                .build();\n\n        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\n\n        var tokenData = mapper.readValue(response.body(), Map.class);\n        accessToken = (String) tokenData.get(\"access_token\");\n        int expiresIn = (Integer) tokenData.get(\"expires_in\");\n        expirationTime = Instant.now().plusSeconds(expiresIn);\n\n        return accessToken;\n    }\n}\n","lang":"java"},"children":[]}]},{"$$mdtype":"Tag","name":"div","attributes":{"label":"C#","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"csharp","header":{"controls":{"copy":{}}},"source":"using System.Net.Http.Json;\nusing System.Text.Json;\n\npublic class TokenManager\n{\n    private static string? _accessToken;\n    private static DateTime _expirationTime;\n    private static readonly HttpClient Client = new();\n\n    public static async Task<string> GetAccessTokenAsync()\n    {\n        if (_accessToken != null && DateTime.UtcNow < _expirationTime.AddSeconds(-60))\n            return _accessToken!;\n\n        var payload = new\n        {\n            grant_type = \"client_credentials\",\n            client_id = Environment.GetEnvironmentVariable(\"BOOKABLE_CLIENT_ID\"),\n            client_secret = Environment.GetEnvironmentVariable(\"BOOKABLE_CLIENT_SECRET\"),\n            audience = \"api.bookabletech.com\"\n        };\n\n        var response = await Client.PostAsJsonAsync(\"https://auth.bookabletech.com/oauth/token\", payload);\n        response.EnsureSuccessStatusCode();\n\n        var content = await response.Content.ReadAsStringAsync();\n        using var doc = JsonDocument.Parse(content);\n\n        _accessToken = doc.RootElement.GetProperty(\"access_token\").GetString();\n        var expiresIn = doc.RootElement.GetProperty(\"expires_in\").GetInt32();\n        _expirationTime = DateTime.UtcNow.AddSeconds(expiresIn);\n\n        return _accessToken!;\n    }\n}\n","lang":"csharp"},"children":[]}]},{"$$mdtype":"Tag","name":"div","attributes":{"label":"Ruby","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"ruby","header":{"controls":{"copy":{}}},"source":"require 'net/http'\nrequire 'json'\nrequire 'time'\n\nclass TokenManager\n  @@access_token = nil\n  @@expiration_time = Time.now\n\n  def self.get_access_token\n    if @@access_token.nil? || Time.now >= (@@expiration_time - 60)\n      uri = URI(\"https://auth.bookabletech.com/oauth/token\")\n      request = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')\n\n      request.body = {\n        grant_type: 'client_credentials',\n        client_id: ENV['BOOKABLE_CLIENT_ID'],\n        client_secret: ENV['BOOKABLE_CLIENT_SECRET'],\n        audience: 'api.bookabletech.com'\n      }.to_json\n\n      response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|\n        http.request(request)\n      end\n\n      data = JSON.parse(response.body)\n      @@access_token = data[\"access_token\"]\n      @@expiration_time = Time.now + data[\"expires_in\"].to_i\n    end\n\n    @@access_token\n  end\nend\n","lang":"ruby"},"children":[]}]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"response-reference","__idx":2},"children":["Response reference"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"access_token\": \"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...\",\n  \"scope\": \"venue:read venue-booking:create\",\n  \"expires_in\": 3600,\n  \"token_type\": \"Bearer\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Field"},"children":["Field"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["access_token"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["JWT to include in every API request as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Authorization: Bearer <token>"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["scope"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Permissions granted: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["venue:read"]}," (search venues), ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["venue-booking:create"]}," (create bookings)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["expires_in"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Token lifetime in seconds (typically 3600). Always read this — do not hardcode it."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["token_type"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Always ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Bearer"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"Card","attributes":{"title":"🔔 Important"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Do not hardcode the expiration time — always read and store the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["expires_in"]}," value from the response."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"next-steps","__idx":3},"children":["Next steps"]},{"$$mdtype":"Tag","name":"Cards","attributes":{},"children":[{"$$mdtype":"Tag","name":"Card","attributes":{"title":"Quickstart","href":"/getting-started/quickstart"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Make your first booking end-to-end in 5 steps"]}]},{"$$mdtype":"Tag","name":"Card","attributes":{"title":"Core Concepts","href":"/getting-started/concepts"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Understand venues, composite IDs, and booking types"]}]},{"$$mdtype":"Tag","name":"Card","attributes":{"title":"API Reference","href":"/apis/production/bookingapi"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Full endpoint documentation"]}]},{"$$mdtype":"Tag","name":"Card","attributes":{"title":"Webhooks","href":"/resources/webhook"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Receive real-time booking status updates"]}]}]}]},"headings":[{"value":"Overview","id":"overview","depth":2},{"value":"Token endpoint","id":"token-endpoint","depth":2},{"value":"Response reference","id":"response-reference","depth":2},{"value":"Next steps","id":"next-steps","depth":2}],"frontmatter":{"seo":{"title":"Overview"}},"lastModified":"2026-03-04T17:44:48.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/getting-started/auth","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}