{"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"api-docs-hooks","__idx":0},"children":["API docs hooks"]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"danger","name":"Deprecated docs"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The developer portal beta is ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"/product-timelines"},"children":["approaching end of life"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use Realm and Reunite instead. Read the ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"/docs-legacy/developer-portal/migrate-from-legacy-portal"},"children":["migration guide"]},"."]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning","name":"Important"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["API docs hooks are supported from version ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["1.1.0-beta.28"]},"."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Hooks give you greater control over some parts of integrated API docs in your developer portal."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use API docs hooks to:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"Link","attributes":{"href":"#api-docs-hooks"},"children":["API docs hooks"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"Link","attributes":{"href":"#steps"},"children":["Steps"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"Link","attributes":{"href":"#example-1-install-the-try-it-request-interceptor"},"children":["Example 1: Install the \"Try it\" request interceptor"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"Link","attributes":{"href":"#example-2-preset-values-of-try-it-parameters-based-on-user-claims"},"children":["Example 2: Preset values of \"Try it\" parameters based on user claims"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"Link","attributes":{"href":"#example-3-override-the-try-it-console-security-panel"},"children":["Example 3: Override the \"Try it\" console Security panel"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"Link","attributes":{"href":"#example-4-install-analytics-on-try-it-panel-events"},"children":["Example 4: Install analytics on \"Try it\" panel events"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"Link","attributes":{"href":"#example-5-add-the-beta-badge-to-operations"},"children":["Example 5: Add the Beta badge to operations"]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"steps","__idx":1},"children":["Steps"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Create a folder called ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["_override"]}," in the root of your portal project (if it doesn't already exist)."," ","The underscore at the beginning of the folder name means the folder is treated as \"hidden\", and the content inside the folder is not accessible to your portal visitors."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["In the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["_override"]}," folder, create a file called ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ReferenceDocsHooks.tsx"]},"."," ","Define your hooks in this file."," ","The examples in this guide contain working code that you can add to your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ReferenceDocsHooks.tsx"]}," file."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Save changes to the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ReferenceDocsHooks.tsx"]}," file."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Make sure to restart your portal development server (or rebuild your portal project) for changes to apply."]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"example-1-install-the-try-it-request-interceptor","__idx":2},"children":["Example 1: Install the \"Try it\" request interceptor"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"tsx","header":{"controls":{"copy":{}}},"source":"export function requestInterceptor(req, operation) {\n  console.log('Request:', req, rawOperation);\n\n  // you get the operation model with raw operation info from the OAS definition\n  const rawOperation = operation.operationDefinition;\n\n  // you can manipulate headers, e.g. inject header based on req body\n  req.headers['x-body-length'] = req.body?.length;\n\n  // you can also change the req URL\n  req.url = '/proxy' + req.url;\n\n  return req;\n}\n","lang":"tsx"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"example-2-preset-values-of-try-it-parameters-based-on-user-claims","__idx":3},"children":["Example 2: Preset values of \"Try it\" parameters based on user claims"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["In addition to parameter values, this example shows how to preset security details in the ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["Try it"]}," console."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"tsx","header":{"controls":{"copy":{}}},"source":"import { setParameterValue, setSecurityDetails, getUserClaims } from '@redocly/developer-portal/ui';\n\nexport function onInit() {\n  const claims = getUserClaims(); // you can use user claims if login is enabled or get value from other place\n\n  // setParameterValue(in, name, value)\n  setParameterValue('path', 'petId', 25);\n  setParameterValue('header', 'x-user-email', claims?.email);\n\n  setSecurityDetails('api_key', 'sk_123123'); // 'api_key' is the security scheme id from the OAS definition\n  setSecurityDetails('my_oauth2', {\n    client_id: 'user1',\n    client_secret: 'secret123'\n  });\n}\n","lang":"tsx"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"example-3-override-the-try-it-console-security-panel","__idx":4},"children":["Example 3: Override the \"Try it\" console Security panel"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This is a basic example of the custom Security panel which loads a list of apps with API keys based on user identity, and lets the user choose between them."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Add any React component to the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["_override/ReferenceDocsHooks.tsx"]}," file."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The key is to call ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onChange"]}," callback to provide auth details back to Reference docs ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["Try it"]}," panel."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"tsx","header":{"controls":{"copy":{}}},"source":"import * as React from 'react';\nimport { useState } from 'react';\nimport { SecurityPanelHookProps, getIdPJwt, getUserClaims, Dropdown } from '@redocly/developer-portal/ui';\n\nexport function ReplaceTryItSecurityPanel({ operation, server, onChange, OAuth2 }: SecurityPanelHookProps) {\n  const [error, setError] = useState(null);\n  const [apps, setApps] = useState([]);\n  const [loading, setLoading] = useState(false);\n  const [tokenLoading, setTokenLoading] = useState(false);\n  const [selectedAppIdx, setSelectedAppIdx] = useState<any>(-1);\n\n  // let's take the first scheme for example\n  const authScheme = operation.security[0].schemes[0];\n\n  const userIdPJwt = getIdPJwt();\n  const userClaims = getUserClaims();\n\n  React.useEffect(() => {\n    run();\n    async function run() {\n      setLoading(true);\n      try {\n        // fetch apps based on user token\n        const apps = fetch('...', {\n          headers: { Authorization: userIdPJwt }\n        });\n\n        setApps(apps);\n\n        setSelectedAppIdx(-1);\n        setLoading(false);\n      } catch (e) {\n        setError(e.message);\n        setLoading(false);\n      }\n    }\n  }, []);\n\n\n\n  const handleSelect = item => {\n    setSelectedAppIdx(item.idx);\n\n    // call onChange to pass auth details back ot reference docs\n    // pass string for API Key\n    onChange({\n      [authScheme.id]: item.accessKy\n    });\n\n    // for basic auth\n    // onChange({\n    //   [authScheme.id]: { username: 'xxx', password: 'xxx' },\n    // });\n\n    // for oauth2\n    // onChange({\n    //   [authScheme.id]: { token: { access_token: 'test' } },\n    // });\n  };\n\n  if (loading) {\n    return 'Loading apps...';\n  }\n\n  const options = apps.map((app, idx) => ({ idx, value: app.name, key: app.accessKey }));\n\n  return (\n    <div>\n      <h4 style={{marginTop: 0}}> Select app: </h4>\n      <Dropdown\n        fullWidth\n        variant=\"dark\"\n        onChange={handleSelect}\n        value={options[selectedAppIdx]?.value}\n        options={options}\n      />\n      {error}\n    </div>\n  );\n}\n","lang":"tsx"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Simplify the OAuth2 token exchange with our helper library."]},{"$$mdtype":"Tag","name":"Tabs","attributes":{"size":"medium"},"children":[{"$$mdtype":"Tag","name":"TabItemFragment","attributes":{"label":"authorizeClientCredentials","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"js","header":{"controls":{"copy":{}}},"source":"OAuth2.authorizeClientCredentials({\n  tokenUrl: authScheme.flows.clientCredentials.tokenUrl,\n  clientId: apps[selectedAppIdx].client_id,\n  clientSecret: apps[selectedAppIdx].client_secret,\n  scopes: [],\n  successCallback: token => {\n    setTokenLoading(false);\n    onChange({\n      [authScheme.id]: { token },\n    });\n  },\n  errorCallback: e => {\n    setTokenLoading(false);\n    setError(e.message);\n  },\n});\n","lang":"js"},"children":[]}]},{"$$mdtype":"Tag","name":"TabItemFragment","attributes":{"label":"authorizeAuthorizationCode","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"js","header":{"controls":{"copy":{}}},"source":"OAuth2.authorizeAuthorizationCode({\n  authorizationUrl: authScheme.flows.clientCredentials.authorizationUrl,\n  tokenUrl: authScheme.flows.clientCredentials.tokenUrl,\n  clientId: apps[selectedAppIdx].client_id,\n  clientSecret: apps[selectedAppIdx].client_secret,\n  scopes: [],\n  successCallback: token => {\n    setTokenLoading(false);\n    onChange({\n      [authScheme.id]: { token },\n    });\n  },\n  errorCallback: e => {\n    setTokenLoading(false);\n    setError(e.message);\n  },\n});\n","lang":"js"},"children":[]}]},{"$$mdtype":"Tag","name":"TabItemFragment","attributes":{"label":"authorizeImplicit","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"js","header":{"controls":{"copy":{}}},"source":"OAuth2.authorizeImplicit({\n  authorizationUrl: authScheme.flows.clientCredentials.authorizationUrl,\n  clientId: apps[selectedAppIdx].client_id,\n  scopes: [],\n  successCallback: token => {\n    setTokenLoading(false);\n    onChange({\n      [authScheme.id]: { token },\n    });\n  },\n  errorCallback: e => {\n    setTokenLoading(false);\n    setError(e.message);\n  },\n});\n","lang":"js"},"children":[]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"example-4-install-analytics-on-try-it-panel-events","__idx":5},"children":["Example 4: Install analytics on \"Try it\" panel events"]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning","name":"Important"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To use this hook, you must enable corresponding ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"/docs-legacy/developer-portal/configuration/siteconfig/analytics"},"children":["analytics plugins"]}," in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["siteConfig.yaml"]}," of your portal."]}]},{"$$mdtype":"Tag","name":"Tabs","attributes":{"size":"medium"},"children":[{"$$mdtype":"Tag","name":"TabItemFragment","attributes":{"label":"Google Analytics: _override/ReferenceDocsHooks.tsx","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"tsx","header":{"controls":{"copy":{}}},"source":"declare global {\n  // make Typescript happy\n  interface Window { ga: any; }\n}\nexport const events = {\n  tryItOpen: (event) => { // both left and right panels\n    console.log('Event:', event);\n    // Google Analytics available only in prod mode\n    if (!window.ga) return;\n    window.ga(`send`, `event`, {\n      eventCategory: event.eventType,\n      eventAction: event.action,\n      eventLabel: event.resource,\n    });\n  },\n  tryItSent: (event) => {\n    console.log('Event:', event);\n    // Google Analytics available only in prod mode\n    if (!window.ga) return;\n    window.ga(`send`, `event`, {\n      eventCategory: event.eventType,\n      eventAction: event.action,\n      eventLabel: event.resource,\n    })\n  },\n  // ...\n}\n","lang":"tsx"},"children":[]}]},{"$$mdtype":"Tag","name":"TabItemFragment","attributes":{"label":"Amplitude Analytics: _override/ReferenceDocsHooks.tsx","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"tsx","header":{"controls":{"copy":{}}},"source":"declare global {\n  // make Typescript happy\n  interface Window { amplitude: any; }\n}\n\nexport const events = {\n  tryItOpen: (event) => { // both left and right panels\n    console.log('Event:', event);\n    // Amplitude Analytics is available only in prod mode\n    if (!window.amplitude) return;\n    const { eventType, ...eventProps } = event;\n    window.amplitude.getInstance().logEvent(eventType, eventProps);\n  },\n  tryItSent: (event) => {\n    console.log('Event:', event);\n    // Amplitude Analytics is available only in prod mode\n    if (!window.amplitude) return;\n    const { eventType, ...eventProps } = event;\n    window.amplitude.getInstance().logEvent(eventType, eventProps);\n  },\n  // ...\n}\n","lang":"tsx"},"children":[]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For the full list of available events refer to ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"/docs-legacy/api-reference-docs/configuration/functionality"},"children":["Reference docs configuration for events"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"example-5-add-the-beta-badge-to-operations","__idx":6},"children":["Example 5: Add the Beta badge to operations"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This example shows how to add a \"Beta\" badge to an operation in your Reference docs."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Before setting up the hook in your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ReferenceDocsHooks.tsx"]}," file, you must first add the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x-beta: true"]}," property to the operation in your OpenAPI document."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The following excerpt from an OpenAPI document shows an operation with the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x-beta: true"]}," property."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"yaml","header":{"controls":{"copy":{}}},"source":"paths:\n  /pet:\n    put:\n      summary: Update an existing pet222\n      description: 'This operation is currently in beta - work in progress!'\n      operationId: updatePet\n      x-beta: true\n      security:\n        - petstore_auth:\n            - 'write:pets'\n            - 'read:pets'\n      responses:\n        '400':\n          description: Invalid ID supplied\n        '404':\n          description: Pet not found\n        '405':\n          description: Validation exception\n      requestBody:\n        $ref: '#/components/requestBodies/Pet'\n","lang":"yaml"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can add this property to multiple operations in your OpenAPI document."," ","Make sure you're adding it to the correct OpenAPI file - the one that's ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"/docs-legacy/developer-portal/guides/reference-docs-integration"},"children":["integrated into your portal"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Next, set up the hook by adding the following code to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ReferenceDocsHooks.tsx"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"tsx","header":{"controls":{"copy":{}}},"source":"import * as React from 'react';\nimport styled from 'styled-components';\n\nexport function AfterOperationSummary({ operation }) {\n  // you get the operation model with raw operation info from the OAS definition\n  const rawOperation = operation.operationDefinition;\n  if (rawOperation['x-beta']) {\n    return <Badge> beta </Badge>;\n  } else {\n    return null;\n  }\n}\n\nconst Badge = styled.span`\n  background: #e1850f;\n  border-radius: 5px;\n  margin-left: 10px;\n  padding: 2px 10px;\n  font-size: 14px;\n  vertical-align: super;\n  color: white;\n`;\n","lang":"tsx"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The \"Beta\" badge is displayed in your API docs next to the operation summary."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"Image","attributes":{"src":"/content-assets/reference-hooks-beta-badge-99f372f92404b597.png","alt":"Operation in API docs with a beta badge"},"children":[]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To change the appearance of the \"Beta\" badge, modify the CSS values under ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["const Badge = styled.span"]},"."," ","In this example, we used ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["background"]}," to set the color of the badge to orange, and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["color"]}," to make the \"Beta\" text white."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To change the text displayed on the badge, modify the contents of the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<Badge></Badge>"]}," tag."," ","In this example, we used ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["beta"]}," as the text, but you might want to make it uppercase (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Beta"]},")."]}]},"frontmatter":{"excludeFromSearch":true},"tagList":["admonition","partial","tab","tabs"],"title":"API docs hooks","lastModified":"2025-05-28T16:01:32.000Z"}