> ## Documentation Index
> Fetch the complete documentation index at: https://docs.goguava.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Guava Architecture Overview

> How Guava's Dialog System and your Expert work together on every call

export const ExpertAnimation = () => {
  const VIEWBOX_WIDTH = 840;
  const VIEWBOX_HEIGHT = 220;
  const GAP = 24;
  const MESSAGE_WIDTH = 210;
  const MESSAGE_PADDING_X = 12;
  const MESSAGE_RADIUS = 9;
  const NIB_WIDTH = 10;
  const NIB_HEIGHT = 12;
  const ANIMATION_DURATION = 14;
  const CALLER_CENTER_X = 50;
  const CALLER_HEAD_Y = 85;
  const CALLER_HEAD_RADIUS = 17;
  const CALLER_BODY_HALF_WIDTH = 29;
  const CALLER_NECK_GAP = 4;
  const CALLER_BODY_HEIGHT = 36;
  const CALLER_BODY_TOP_Y = CALLER_HEAD_Y + CALLER_HEAD_RADIUS + CALLER_NECK_GAP;
  const CALLER_BODY_BOTTOM_Y = CALLER_BODY_TOP_Y + CALLER_BODY_HEIGHT;
  const CALLER_LABEL_Y = CALLER_BODY_BOTTOM_Y + 24;
  const CALLER_RIGHT_EDGE = CALLER_CENTER_X + CALLER_BODY_HALF_WIDTH;
  const LEFT_MESSAGE_X = CALLER_RIGHT_EDGE + GAP;
  const ACTOR_WIDTH = 112;
  const ACTOR_HEIGHT = 120;
  const ACTOR_Y = (VIEWBOX_HEIGHT - ACTOR_HEIGHT) / 2;
  const ACTOR_RADIUS = 8;
  const ACTOR_LABEL_FONT_SIZE = 10;
  const ACTOR_LABEL_Y = (VIEWBOX_HEIGHT + ACTOR_LABEL_FONT_SIZE) / 2;
  const DIALOG_ENGINE_X = LEFT_MESSAGE_X + MESSAGE_WIDTH + GAP;
  const RIGHT_MESSAGE_X = DIALOG_ENGINE_X + ACTOR_WIDTH + GAP;
  const EXPERT_X = RIGHT_MESSAGE_X + MESSAGE_WIDTH + GAP;
  const MESSAGES_START_Y = 43;
  const MESSAGE_X = {
    left: LEFT_MESSAGE_X,
    right: RIGHT_MESSAGE_X
  };
  const MESSAGE_FILL = {
    primary: "rgba(26,147,254,0.14)",
    secondary: "rgba(255,255,255,0.08)"
  };
  const MESSAGE_STROKE = {
    primary: "rgba(26,147,254,0.45)",
    secondary: "rgba(255,255,255,0.22)"
  };
  const animationSecs = seconds => seconds / ANIMATION_DURATION * 100;
  const messageAnimation = (name, hiddenUntil) => {
    const hiddenStop = hiddenUntil === 0 ? "0%" : `0%, ${animationSecs(hiddenUntil)}%`;
    const visibleAt = animationSecs(hiddenUntil + 0.4);
    return `
      @keyframes ${name} {
       ${hiddenStop} { opacity: 0; transform: translateY(6px) scale(0.98); }
       ${visibleAt}% { opacity: 1; transform: translateY(0px) scale(1); }
       90%           { opacity: 1; transform: translateY(0px) scale(1); }
       97%, 100%     { opacity: 0; transform: translateY(-4px) scale(0.98); }
      }
      .${name} { animation: ${name} ${ANIMATION_DURATION}s ease-in-out infinite; }
    `;
  };
  const Lines = ({x, lines}) => lines.map((line, i) => <tspan key={i} x={x} dy={i === 0 ? 0 : "1.4em"}>{line}</tspan>);
  const MessageBubble = ({animationClass, column, y, height, fontSize, tone, nibSide, text}) => {
    const x = MESSAGE_X[column];
    const textX = x + MESSAGE_PADDING_X;
    const lineHeight = fontSize * 1.4;
    const textBlockHeight = (text.length - 1) * lineHeight + fontSize;
    const textY = y + (height - textBlockHeight) / 2 + fontSize - 1;
    const nibCenterY = y + height / 2;
    const bubbleRight = x + MESSAGE_WIDTH;
    const bubbleBottom = y + height;
    const bubblePath = nibSide === "left" ? [`M ${x + MESSAGE_RADIUS} ${y}`, `H ${bubbleRight - MESSAGE_RADIUS}`, `Q ${bubbleRight} ${y} ${bubbleRight} ${y + MESSAGE_RADIUS}`, `V ${bubbleBottom - MESSAGE_RADIUS}`, `Q ${bubbleRight} ${bubbleBottom} ${bubbleRight - MESSAGE_RADIUS} ${bubbleBottom}`, `H ${x + MESSAGE_RADIUS}`, `Q ${x} ${bubbleBottom} ${x} ${bubbleBottom - MESSAGE_RADIUS}`, `V ${nibCenterY + NIB_HEIGHT / 2}`, `L ${x - NIB_WIDTH} ${nibCenterY}`, `L ${x} ${nibCenterY - NIB_HEIGHT / 2}`, `V ${y + MESSAGE_RADIUS}`, `Q ${x} ${y} ${x + MESSAGE_RADIUS} ${y}`, "Z"].join(" ") : [`M ${x + MESSAGE_RADIUS} ${y}`, `H ${bubbleRight - MESSAGE_RADIUS}`, `Q ${bubbleRight} ${y} ${bubbleRight} ${y + MESSAGE_RADIUS}`, `V ${nibCenterY - NIB_HEIGHT / 2}`, `L ${bubbleRight + NIB_WIDTH} ${nibCenterY}`, `L ${bubbleRight} ${nibCenterY + NIB_HEIGHT / 2}`, `V ${bubbleBottom - MESSAGE_RADIUS}`, `Q ${bubbleRight} ${bubbleBottom} ${bubbleRight - MESSAGE_RADIUS} ${bubbleBottom}`, `H ${x + MESSAGE_RADIUS}`, `Q ${x} ${bubbleBottom} ${x} ${bubbleBottom - MESSAGE_RADIUS}`, `V ${y + MESSAGE_RADIUS}`, `Q ${x} ${y} ${x + MESSAGE_RADIUS} ${y}`, "Z"].join(" ");
    return <g className={`ea-msg ${animationClass}`}>
        <path d={bubblePath} fill={MESSAGE_FILL[tone]} stroke={MESSAGE_STROKE[tone]} strokeWidth="1" />
        <text x={textX} y={textY} fill={tone === "primary" ? "#d7ecff" : "#dcdcdc"} fontSize={fontSize} fontFamily="ui-monospace, monospace">
          <Lines x={textX} lines={text} />
        </text>
      </g>;
  };
  return <svg viewBox={`4 36 ${VIEWBOX_WIDTH} ${VIEWBOX_HEIGHT - 73}`} xmlns="http://www.w3.org/2000/svg" className="w-full my-8">
      <defs>
        <style>{`
          ${messageAnimation("ea-msg-1", 0)}
          ${messageAnimation("ea-msg-2", 2)}
          ${messageAnimation("ea-msg-3", 2)}
          ${messageAnimation("ea-msg-4", 6)}
          ${messageAnimation("ea-msg-5", 8)}

          .ea-msg {
           opacity: 0;
           transform-box: fill-box;
           transform-origin: center;
          }
        `}</style>
      </defs>

      {}
      <circle cx={CALLER_CENTER_X} cy={CALLER_HEAD_Y} r={CALLER_HEAD_RADIUS} fill="#272728" stroke="rgba(255,255,255,0.18)" strokeWidth="1.5" />
      <path d={`M ${CALLER_CENTER_X - CALLER_BODY_HALF_WIDTH} ${CALLER_BODY_BOTTOM_Y} C ${CALLER_CENTER_X - CALLER_BODY_HALF_WIDTH} ${CALLER_BODY_TOP_Y + 7} ${CALLER_CENTER_X - 13} ${CALLER_BODY_TOP_Y} ${CALLER_CENTER_X} ${CALLER_BODY_TOP_Y} C ${CALLER_CENTER_X + 13} ${CALLER_BODY_TOP_Y} ${CALLER_CENTER_X + CALLER_BODY_HALF_WIDTH} ${CALLER_BODY_TOP_Y + 7} ${CALLER_CENTER_X + CALLER_BODY_HALF_WIDTH} ${CALLER_BODY_BOTTOM_Y}`} fill="#272728" stroke="rgba(255,255,255,0.18)" strokeWidth="1.5" strokeLinecap="round" />
      <text x={CALLER_CENTER_X} y={CALLER_LABEL_Y} textAnchor="middle" fill="#acacac" fontSize={ACTOR_LABEL_FONT_SIZE} fontFamily="ui-monospace, monospace" fontWeight="700">
        Caller
      </text>

      {}
      <rect x={DIALOG_ENGINE_X} y={ACTOR_Y} width={ACTOR_WIDTH} height={ACTOR_HEIGHT} rx={ACTOR_RADIUS} fill="#272728" stroke="rgba(26,147,254,0.4)" strokeWidth="1.5" />
      <text x={DIALOG_ENGINE_X + ACTOR_WIDTH / 2} y={ACTOR_LABEL_Y} textAnchor="middle" fill="#1a93fe" fontSize={ACTOR_LABEL_FONT_SIZE} fontFamily="ui-monospace, monospace" fontWeight="700">
        Dialog System
      </text>

      {}
      <rect x={EXPERT_X} y={ACTOR_Y} width={ACTOR_WIDTH} height={ACTOR_HEIGHT} rx={ACTOR_RADIUS} fill="#272728" stroke="rgba(255,255,255,0.15)" strokeWidth="1.5" />
      <text x={EXPERT_X + ACTOR_WIDTH / 2} y={ACTOR_LABEL_Y} textAnchor="middle" fill="#dadada" fontSize={ACTOR_LABEL_FONT_SIZE} fontFamily="ui-monospace, monospace" fontWeight="700">
        Expert
      </text>

      <MessageBubble animationClass="ea-msg-1" column="left" y={MESSAGES_START_Y} height={30} fontSize={8} tone="secondary" nibSide="left" text={["Do your policies cover flood damage?"]} />

      <MessageBubble animationClass="ea-msg-2" column="left" y={MESSAGES_START_Y + 46} height={30} fontSize={8} tone="secondary" nibSide="right" text={["Let me look into that for you."]} />

      <MessageBubble animationClass="ea-msg-3" column="right" y={MESSAGES_START_Y + 46} height={30} fontSize={8} tone="primary" nibSide="left" text={['on_question("Do your policies...")']} />

      <MessageBubble animationClass="ea-msg-4" column="right" y={MESSAGES_START_Y + 92} height={40} fontSize={7.5} tone="secondary" nibSide="right" text={["ANSWER: No, loss or damage from flood,", "surface water, waves, storm surge..."]} />

      <MessageBubble animationClass="ea-msg-5" column="left" y={MESSAGES_START_Y + 92} height={40} fontSize={7.3} tone="secondary" nibSide="right" text={["Our standard policies typically do not", "cover loss or damage from floods or..."]} />
    </svg>;
};

export const ArchitectureDiagram = () => <svg viewBox="32 46 695 222" xmlns="http://www.w3.org/2000/svg" className="w-full my-8">
    <defs>
      <marker id="ahd" markerWidth="8" markerHeight="7" refX="7" refY="3.5" orient="auto">
        <polygon points="0 0, 8 3.5, 0 7" fill="rgba(255,255,255,0.25)" />
      </marker>
    </defs>

    {}
    <rect x="144" y="47" width="256" height="130" rx="14" fill="#1e1f21" stroke="rgba(255,255,255,0.08)" strokeWidth="1.5" />
    <text x="272" y="65" textAnchor="middle" fill="#555558" fontSize="9" fontFamily="ui-monospace, monospace" letterSpacing="2">GUAVA CLOUD</text>

    {}
    <rect x="160" y="72" width="224" height="82" rx="8" fill="#272728" stroke="rgba(26,147,254,0.3)" strokeWidth="1.5" />
    <text x="272" y="110" textAnchor="middle" fill="#1a93fe" fontSize="14" fontFamily="ui-monospace, monospace" fontWeight="700">Dialog System</text>
    <text x="272" y="132" textAnchor="middle" fill="#555558" fontSize="10" fontFamily="ui-monospace, monospace">audio · STT · LLM · TTS</text>

    {}
    <circle cx="65" cy="115" r="32" fill="#272728" stroke="rgba(255,255,255,0.1)" strokeWidth="1.5" />
    {}
    <path d="M 51,113 C 51,103 58,97 65,97 C 72,97 79,103 79,113" fill="none" stroke="#dadada" strokeWidth="1.8" strokeLinecap="round" />
    <rect x="47" y="113" width="8" height="12" rx="4" fill="#272728" stroke="#dadada" strokeWidth="1.5" />
    <rect x="75" y="113" width="8" height="12" rx="4" fill="#272728" stroke="#dadada" strokeWidth="1.5" />
    <text x="65" y="168" textAnchor="middle" fill="#acacac" fontSize="11" fontFamily="ui-monospace, monospace">Caller</text>

    {}
    <line x1="97" y1="111" x2="137" y2="111" stroke="#acacac" strokeWidth="1.5" />
    <polygon points="144,111 137,109 137,113" fill="#acacac" />
    <line x1="104" y1="119" x2="144" y2="119" stroke="#acacac" strokeWidth="1.5" />
    <polygon points="97,119 104,117 104,121" fill="#acacac" />
    <text x="120" y="103" textAnchor="middle" fill="#555558" fontSize="9" fontFamily="ui-monospace, monospace">audio</text>

    {}
    <line x1="400" y1="111" x2="458" y2="111" stroke="#acacac" strokeWidth="1.5" />
    <polygon points="465,111 458,109 458,113" fill="#acacac" />
    <line x1="407" y1="119" x2="465" y2="119" stroke="#acacac" strokeWidth="1.5" />
    <polygon points="400,119 407,117 407,121" fill="#acacac" />
    <text x="432" y="103" textAnchor="middle" fill="#555558" fontSize="9" fontFamily="ui-monospace, monospace">WebSocket</text>

    {}
    <rect x="465" y="72" width="186" height="82" rx="8" fill="#272728" stroke="rgba(255,255,255,0.12)" strokeWidth="1.5" />
    <text x="558" y="108" textAnchor="middle" fill="#dadada" fontSize="13" fontFamily="ui-monospace, monospace" fontWeight="700">Your Expert</text>
    <text x="558" y="130" textAnchor="middle" fill="#555558" fontSize="10" fontFamily="ui-monospace, monospace">Python · TypeScript · ...</text>

    {}
    <line x1="558" y1="154" x2="465" y2="207" stroke="rgba(255,255,255,0.2)" strokeWidth="1.2" strokeDasharray="5 3" markerEnd="url(#ahd)" />
    <line x1="558" y1="154" x2="650" y2="207" stroke="rgba(255,255,255,0.2)" strokeWidth="1.2" strokeDasharray="5 3" markerEnd="url(#ahd)" />

    {}
    <rect x="385" y="210" width="160" height="56" rx="8" fill="#1e1f21" stroke="rgba(255,255,255,0.1)" strokeWidth="1" />
    <text x="465" y="234" textAnchor="middle" fill="#dadada" fontSize="11" fontFamily="ui-monospace, monospace" fontWeight="600">Your Infrastructure</text>
    <text x="465" y="252" textAnchor="middle" fill="#555558" fontSize="9.5" fontFamily="ui-monospace, monospace">local or self-hosted</text>

    {}
    <text x="559" y="242" textAnchor="middle" fill="#555558" fontSize="9" fontFamily="ui-monospace, monospace">or</text>

    {}
    <rect x="573" y="210" width="152" height="56" rx="8" fill="#1e1f21" stroke="rgba(26,147,254,0.35)" strokeWidth="1.5" />
    <text x="649" y="234" textAnchor="middle" fill="#dadada" fontSize="11" fontFamily="ui-monospace, monospace" fontWeight="600">Guava Hosting</text>
    <text x="649" y="252" textAnchor="middle" fill="#555558" fontSize="9.5" fontFamily="ui-monospace, monospace">managed by Guava</text>

    {}
    <circle r="2.5" fill="#1a93fe">
      <animateMotion dur="1.5s" repeatCount="indefinite" calcMode="linear" path="M 97,111 L 144,111" />
      <animate attributeName="opacity" dur="1.5s" repeatCount="indefinite" keyTimes="0;0.1;0.9;1" values="0;1;1;0" calcMode="linear" />
    </circle>
    <circle r="2.5" fill="#1a93fe">
      <animateMotion dur="1.5s" repeatCount="indefinite" calcMode="linear" begin="-0.75s" path="M 97,111 L 144,111" />
      <animate attributeName="opacity" dur="1.5s" repeatCount="indefinite" begin="-0.75s" keyTimes="0;0.1;0.9;1" values="0;1;1;0" calcMode="linear" />
    </circle>
    <circle r="2.5" fill="#1a93fe">
      <animateMotion dur="1.5s" repeatCount="indefinite" calcMode="linear" path="M 144,119 L 97,119" />
      <animate attributeName="opacity" dur="1.5s" repeatCount="indefinite" keyTimes="0;0.1;0.9;1" values="0;1;1;0" calcMode="linear" />
    </circle>
    <circle r="2.5" fill="#1a93fe">
      <animateMotion dur="1.5s" repeatCount="indefinite" calcMode="linear" begin="-0.75s" path="M 144,119 L 97,119" />
      <animate attributeName="opacity" dur="1.5s" repeatCount="indefinite" begin="-0.75s" keyTimes="0;0.1;0.9;1" values="0;1;1;0" calcMode="linear" />
    </circle>

    {}
    <circle r="2.5" fill="#1a93fe">
      <animateMotion dur="1.5s" repeatCount="indefinite" calcMode="linear" path="M 400,111 L 465,111" />
      <animate attributeName="opacity" dur="1.5s" repeatCount="indefinite" keyTimes="0;0.1;0.9;1" values="0;1;1;0" calcMode="linear" />
    </circle>
    <circle r="2.5" fill="#1a93fe">
      <animateMotion dur="1.5s" repeatCount="indefinite" calcMode="linear" begin="-0.75s" path="M 400,111 L 465,111" />
      <animate attributeName="opacity" dur="1.5s" repeatCount="indefinite" begin="-0.75s" keyTimes="0;0.1;0.9;1" values="0;1;1;0" calcMode="linear" />
    </circle>
    <circle r="2.5" fill="#1a93fe">
      <animateMotion dur="1.5s" repeatCount="indefinite" calcMode="linear" path="M 465,119 L 400,119" />
      <animate attributeName="opacity" dur="1.5s" repeatCount="indefinite" keyTimes="0;0.1;0.9;1" values="0;1;1;0" calcMode="linear" />
    </circle>
    <circle r="2.5" fill="#1a93fe">
      <animateMotion dur="1.5s" repeatCount="indefinite" calcMode="linear" begin="-0.75s" path="M 465,119 L 400,119" />
      <animate attributeName="opacity" dur="1.5s" repeatCount="indefinite" begin="-0.75s" keyTimes="0;0.1;0.9;1" values="0;1;1;0" calcMode="linear" />
    </circle>
  </svg>;

Every Guava call involves two systems working in parallel: Guava's hosted **Dialog System** and your **Expert** — a service you provide that connects to Guava's API and steers the conversation.

<ArchitectureDiagram />

## The Dialog System

The Dialog System is Guava's managed service running in the cloud. It handles everything time-sensitive during a call: receiving the caller's audio, transcribing it, synthesizing a response, and streaming it back to the caller.

Because the entire pipeline runs as a fully integrated architecture rather than a chain of off-the-shelf APIs, the Dialog System delivers best-in-class latency and naturalness.

## The Expert

The Expert is the code you write. Using the [Guava SDK](/sdk-reference/agent), it connects to the Dialog System over a persistent WebSocket and steers the agent in real time.

Because your Expert is just code, you can do anything: query a CRM or database, hit an external API, or chain into another specialized AI sub-agent.
The Dialog System always interacts with your Expert asynchronously, so you can spend time on complex tasks and reasoning without the caller ever noticing a pause.

During development, your Expert runs on your local machine, and Guava routes calls to it directly. You can rapidly iterate by changing the code and restarting the process — no public web server or ngrok required.

### Structured Callbacks

<ExpertAnimation />

As the Dialog System converses with the caller in natural language, it maintains a separate communication channel with your Expert.
This channel consists of callbacks that follow a consistent structure and schema, and are designed to be plugged into backend systems like RAG and intent recognition.
You decide which callbacks your Expert can handle — all are optional, and the Dialog System will adapt appropriately.

<CodeGroup>
  ```python Python theme={null}
  @agent.on_question
  def on_question(call: guava.Call, question: str) -> str:
      # The agent will invoke this when the caller asks a question that can't be
      # answered from the current context. Use our ready-to-use RAG module. Or
      # plug in your own custom one.
      return document_qa.ask(question)

  @agent.on_action_request
  def on_action_request(call: guava.Call, request: str) -> list[SuggestedAction]:
      # The agent will invoke this callback when the caller requests a new action.
      # Use our ready-to-use intent classification system. Or plug in a custom one.
      return intent_recognizer.classify(request)
  ```

  ```typescript TypeScript theme={null}
  agent.onQuestion(async (call: guava.Call, question: string) => {
    // The agent will invoke this when the caller asks a question that can't be
    // answered from the current context. Use our ready-to-use RAG module. Or
    // plug in your own custom one.
    return await documentQA.ask(question);
  });

  agent.onActionRequest(async (call: guava.Call, request: string) => {
    // The agent will invoke this callback when the caller requests a new action.
    // Use our ready-to-use intent classification system. Or plug in a custom one.
    return await intentRecognizer.classify(request);
  });
  ```
</CodeGroup>

If you want ready-to-use implementations of these callbacks, you can use our helper library which contains implementations of RAG, intent recognition, and more.
But all of these are optional modules and we encourage you to build your own domain specific versions.

### Assign Tasks to your Agents

Instead of using a single large prompt for your agent, Guava recommends that you use [Tasks](/sdk-reference/tasks).
A task is a checklist of items that you assign to your agent. These items can include things to say to the caller, as well as information to collect as [Fields](/sdk-reference/field).
You will receive a callback when your Agent has completed your task and is awaiting more instructions.

<CodeGroup>
  ```python Python theme={null}
  @agent.on_call_start
  def on_call_start(call: guava.Call):
      call.set_task(
          "waitlist",
          objective="Add callers to the waitlist.",
          checklist=[
              guava.Field(key="caller_name", field_type="text"),
              guava.Field(key="party_size", field_type="integer"),
              guava.Field(key="phone_number", field_type="text"),
              "Read the phone number back to the caller to confirm.",
          ],
      )

  @agent.on_task_complete("waitlist")
  def on_waitlist_done(call: guava.Call):
      print("Added caller to waitlist:", call.get_field("caller_name"))
      # End the call, transfer, or chain into another task.
      call.hangup("Thank the caller and let them know we'll text when their table is ready.")
  ```

  ```typescript TypeScript theme={null}
  agent.onCallStart(async (call) => {
    await call.setTask({
      taskId: "waitlist",
      objective: "Add callers to the waitlist.",
      checklist: [
        guava.Field({ key: "caller_name", fieldType: "text" }),
        guava.Field({ key: "party_size", fieldType: "integer" }),
        guava.Field({ key: "phone_number", fieldType: "text" }),
        "Read the phone number back to the caller to confirm.",
      ],
    });
  });

  agent.onTaskComplete("waitlist", async (call) => {
    console.log("Added caller to waitlist:", await call.getField("caller_name"));
    // End the call, transfer, or chain into another task.
    await call.hangup("Thank the caller and let them know we'll text when their table is ready.");
  });
  ```
</CodeGroup>

## Deploying your Expert

When it's time to move to production, you'll want your Expert deployed in a highly-available configuration, ready to handle calls at any time. Because Guava Experts only make outbound connections, it's easy to run an Expert behind a NAT or firewall.

You have two options for deploying your Expert:

* **Your Infrastructure** — deploy to your own servers, VM, or serverless compute platform. You control the environment.
* **Guava Hosting** — push your Expert with a single [`guava deploy`](/cli-reference/cli-reference) command and Guava manages the rest, including horizontal scaling and redundancy.

See the [Deployment guide](/guides/deployment) for a full walkthrough of both options.

## What to read next

* The [Quickstart](/getting-started/quickstart) shows you how to set up your dev environment and create your first agent.
* The [Example Walkthroughs](/getting-started/inbound-rag-example) show full Expert implementations for common scenarios, including Q\&A and scheduling.
* Once you're comfortable with the basics, the [SDK Reference](/sdk-reference/runner) covers every callback and command in detail.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.