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

> ## Agent Instructions
> Agents: start with https://docs.pre.dev/agents.md, which has complete recipes, plan access, polling rules, errors and limits.
> Authenticate with the workspace API key (pdk_…) from Integrations → Built-in, sent as Authorization: Bearer <key>.
> REST API: https://api.pre.dev (OpenAPI: https://docs.pre.dev/api-reference/openapi.json). AI Gateway: https://api.pre.dev/v1, OpenAI-compatible (OpenAPI: https://docs.pre.dev/api-reference/ai-gateway.openapi.json).
> pre.dev MCP server: https://api.pre.dev/mcp. Search these docs over MCP at https://docs.pre.dev/mcp.

# Audio

> POST /v1/audio/speech for text to speech and music, POST /v1/audio/transcriptions for speech to text.

Both endpoints take the OpenAI-compatible speech and transcription request bodies and return the matching responses. Speech and music models appear in the main [catalog](/ai-gateway/api/models) with `audio` in `architecture.output_modalities`.

## Text to speech

`POST /v1/audio/speech` takes the standard speech body and returns audio bytes. Speech models are `openai/gpt-audio-mini` (fast) and `openai/gpt-audio` (higher quality); voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer` and `verse`. The response is `audio/wav` (24 kHz mono); pass `response_format: "pcm16"` for the raw samples. `instructions` sets the delivery; by default the model reads the text verbatim.

<CodeGroup>
  ```bash curl theme={null}
  curl --fail-with-body https://api.pre.dev/v1/audio/speech \
    -H "Authorization: Bearer $PREDEV_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"model": "openai/gpt-audio-mini", "input": "Your build is ready.", "voice": "alloy"}' \
    -o ready.wav
  ```

  ```typescript Node.js theme={null}
  import fs from 'node:fs/promises';
  import OpenAI from 'openai';

  const client = new OpenAI({ baseURL: 'https://api.pre.dev/v1', apiKey: process.env.PREDEV_API_KEY! });

  const speech = await client.audio.speech.create({
    model: 'openai/gpt-audio-mini',
    input: 'Your build is ready.',
    voice: 'alloy',
  });
  await fs.writeFile('ready.wav', Buffer.from(await speech.arrayBuffer()));
  ```

  ```python Python theme={null}
  import os
  from openai import OpenAI

  client = OpenAI(base_url="https://api.pre.dev/v1", api_key=os.environ["PREDEV_API_KEY"])

  with client.audio.speech.with_streaming_response.create(
      model="openai/gpt-audio-mini",
      input="Your build is ready.",
      voice="alloy",
  ) as speech:
      speech.stream_to_file("ready.wav")
  ```
</CodeGroup>

In a web app, serve the bytes from your own route and point an `<audio>` element at it; the `Content-Type` header names the format. A model id that does not produce audio returns `400` with the list of ids that do.

## Music

The same endpoint composes music: send `model: "google/lyria-3-clip-preview"` (or `google/lyria-3-pro-preview`) with `input` set to the mood or style you want, for example `"upbeat lo-fi for a launch video"`. Music models answer with an MP3 (`audio/mpeg`); `voice` is ignored. The header `x-predev-audio-format` carries the format on every speech response.

## Transcription

`POST /v1/audio/transcriptions` is `multipart/form-data` with a `file` part and a `model` field.

<CodeGroup>
  ```bash curl theme={null}
  curl --fail-with-body https://api.pre.dev/v1/audio/transcriptions \
    -H "Authorization: Bearer $PREDEV_API_KEY" \
    -F "model=$TRANSCRIBE_MODEL" \
    -F "file=@meeting.m4a"
  ```

  ```typescript Node.js theme={null}
  import fs from 'node:fs';
  import OpenAI from 'openai';

  const client = new OpenAI({ baseURL: 'https://api.pre.dev/v1', apiKey: process.env.PREDEV_API_KEY! });

  const transcript = await client.audio.transcriptions.create({
    model: process.env.TRANSCRIBE_MODEL!,
    file: fs.createReadStream('meeting.m4a'),
  });
  console.log(transcript.text);
  ```

  ```python Python theme={null}
  import os
  from openai import OpenAI

  client = OpenAI(base_url="https://api.pre.dev/v1", api_key=os.environ["PREDEV_API_KEY"])

  with open("meeting.m4a", "rb") as audio:
      transcript = client.audio.transcriptions.create(model=os.environ["TRANSCRIBE_MODEL"], file=audio)
  print(transcript.text)
  ```
</CodeGroup>

Set `TRANSCRIBE_MODEL` to a speech-to-text model such as `openai/whisper-large-v3`. Transcription models are not listed by `GET /v1/models`. Speech, music, and transcription calls return the usual `x-predev-*` headers and are charged from the model's metered usage.


## OpenAPI

````yaml api-reference/ai-gateway.openapi.json POST /v1/audio/speech
openapi: 3.1.0
info:
  title: pre.dev AI Gateway API
  version: 1.0.0
  description: >-
    One OpenAI-compatible API for hundreds of models from every major lab, paid
    for in pre.dev credits.


    - **Base URL:** `https://api.pre.dev/v1` for the OpenAI SDKs. The Anthropic
    SDKs take `https://api.pre.dev` and add `/v1/messages` themselves.

    - **Auth:** the workspace API key (`pdk_…`) from Integrations → Built-in in
    the pre.dev dashboard, as `Authorization: Bearer <key>` or `x-api-key`. Keep
    it on a server: it spends the workspace's credits.

    - **Models:** send ids exactly as `GET /v1/models` lists them, such as
    `anthropic/claude-sonnet-5`.

    - **Public directory:** `GET /v1/models/public` lists every model with no
    key and no prices.

    - **Charges:** each call is charged in credits from its metered cost.
    Non-streaming responses report the charge in `x-predev-credits-charged`;
    `GET /v1/generation` reports it for any call. A request is refused with
    `402` before it runs when the balance cannot cover it. Catalog, usage,
    generation and file reads are free.

    - **Limits:** 600 requests per minute per workspace, 30 on the free trial. A
    free workspace can spend 5 credits on AI calls in total, then gets `402
    subscription_required`. Reads other than `GET /v1/files` do not count toward
    the per-minute limit.

    - **Errors:** errors raised by pre.dev use the OpenAI error shape with a
    `predev` object (`GatewayError`). Errors from the model keep their HTTP
    status and message (`ModelError`). Every inference response carries
    `x-predev-request-id`.

    - **Streaming:** `stream: true` returns server-sent events in the native
    format of each endpoint. Lines that start with `:` are keep-alives.


    Example values are illustrative.
  contact:
    name: pre.dev Support
    url: https://pre.dev
    email: support@pre.dev
  license:
    name: Proprietary
    url: https://pre.dev/terms
servers:
  - url: https://api.pre.dev
    description: Production
security:
  - apiKeyAuth: []
  - xApiKey: []
tags:
  - name: Chat
    description: Chat completions, text completions, Responses and Messages.
  - name: Embeddings
    description: Embeddings and rerank.
  - name: Images and video
    description: Image generation and asynchronous video jobs.
  - name: Audio
    description: Speech, music and transcription.
  - name: Files
    description: Documents stored for later chat requests.
  - name: Models
    description: The model catalog, with credit prices.
  - name: Usage
    description: Usage totals and per-call stats.
paths:
  /v1/audio/speech:
    post:
      tags:
        - Audio
      summary: Generate speech or music
      description: >-
        Turns text into speech, or a prompt into music, in the OpenAI speech
        format, so `client.audio.speech.create()` works unchanged. Speech models
        return WAV (24 kHz mono) by default, or raw PCM16 with `response_format:
        "pcm16"`. Music models (`google/lyria-3-clip-preview`,
        `google/lyria-3-pro-preview`) return MP3. The `x-predev-audio-format`
        header names the format. A request needs a balance of at least 1 credit.
      operationId: createSpeech
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SpeechRequest'
            examples:
              speech:
                summary: Speech
                value:
                  model: openai/gpt-audio-mini
                  input: Your build is ready.
                  voice: alloy
              music:
                summary: Music
                value:
                  model: google/lyria-3-clip-preview
                  input: Upbeat lo-fi for a launch video
      responses:
        '200':
          description: >-
            The audio: `audio/wav` for speech, `audio/mpeg` for music, or
            `application/octet-stream` with raw PCM16 samples (24 kHz mono) when
            `response_format` is `pcm16`.
          headers:
            x-predev-request-id:
              $ref: '#/components/headers/RequestId'
            x-predev-credits-charged:
              $ref: '#/components/headers/CreditsCharged'
            x-predev-credits-remaining:
              $ref: '#/components/headers/CreditsRemaining'
            x-predev-audio-format:
              $ref: '#/components/headers/AudioFormat'
          content:
            audio/wav:
              schema:
                type: string
                format: binary
            audio/mpeg:
              schema:
                type: string
                format: binary
            application/octet-stream:
              schema:
                type: string
                format: binary
        '400':
          description: >-
            `model` or `input` is missing, the model does not produce audio, or
            the body is not valid JSON.
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/GatewayError'
                  - $ref: '#/components/schemas/InvalidJsonBody'
              examples:
                missing_fields:
                  summary: Missing model or input
                  value:
                    error:
                      message: >-
                        `model` and `input` (the text to speak, or the music
                        prompt) are required.
                      type: invalid_request_error
                      code: invalid_request
                      param: null
                      predev: {}
                not_audio:
                  summary: Model does not produce audio
                  value:
                    error:
                      message: >-
                        Model openai/tts-1 does not produce audio. Speech and
                        music models available right now:
                        google/lyria-3-pro-preview, google/lyria-3-clip-preview,
                        openai/gpt-audio, openai/gpt-audio-mini.
                      type: invalid_request_error
                      code: invalid_request
                      param: null
                      predev:
                        available_models:
                          - google/lyria-3-pro-preview
                          - google/lyria-3-clip-preview
                          - openai/gpt-audio
                          - openai/gpt-audio-mini
                invalid_json:
                  summary: Body is not valid JSON
                  value:
                    error: Invalid JSON body
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '404':
          $ref: '#/components/responses/ModelNotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
        '502':
          description: The model did not answer or returned no audio.
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/GatewayError'
                  - $ref: '#/components/schemas/ModelError'
              examples:
                no_audio:
                  summary: No audio returned
                  value:
                    error:
                      message: The model returned no audio for this input.
                      type: server_error
                      code: upstream_unavailable
                      param: null
                      predev: {}
                upstream_unavailable:
                  summary: No answer from the model
                  value:
                    error:
                      message: 'The model provider did not answer: timed out'
                      type: server_error
                      code: upstream_unavailable
                      param: null
                      predev: {}
        '503':
          $ref: '#/components/responses/BalanceUnavailable'
      x-codeSamples:
        - lang: cURL
          label: cURL
          source: |-
            curl --fail-with-body https://api.pre.dev/v1/audio/speech \
              -H "Authorization: Bearer $PREDEV_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{"model": "openai/gpt-audio-mini", "input": "Your build is ready.", "voice": "alloy"}' \
              -o ready.wav
        - lang: Python
          label: Python (OpenAI SDK)
          source: >-
            import os

            from openai import OpenAI


            client = OpenAI(base_url="https://api.pre.dev/v1",
            api_key=os.environ["PREDEV_API_KEY"])


            with client.audio.speech.with_streaming_response.create(
                model="openai/gpt-audio-mini",
                input="Your build is ready.",
                voice="alloy",
            ) as speech:
                speech.stream_to_file("ready.wav")
        - lang: JavaScript
          label: Node.js (OpenAI SDK)
          source: >-
            import fs from 'node:fs/promises';

            import OpenAI from 'openai';


            const client = new OpenAI({ baseURL: 'https://api.pre.dev/v1',
            apiKey: process.env.PREDEV_API_KEY });


            const speech = await client.audio.speech.create({
              model: 'openai/gpt-audio-mini',
              input: 'Your build is ready.',
              voice: 'alloy',
            });

            await fs.writeFile('ready.wav', Buffer.from(await
            speech.arrayBuffer()));
components:
  parameters:
    ProjectId:
      name: x-predev-project-id
      in: header
      required: false
      schema:
        type: string
      description: >-
        Attribute this call to one of the workspace's projects. An id the
        workspace does not own is ignored.
  schemas:
    SpeechRequest:
      type: object
      required:
        - model
        - input
      properties:
        model:
          type: string
          description: >-
            A catalog model whose `architecture.output_modalities` includes
            `audio`: speech models (`openai/gpt-audio-mini`, `openai/gpt-audio`)
            or music models (`google/lyria-3-clip-preview`,
            `google/lyria-3-pro-preview`).
          example: openai/gpt-audio-mini
        input:
          type: string
          minLength: 1
          description: >-
            The text to speak, or for a music model, the style or mood to
            compose.
        voice:
          type: string
          default: alloy
          description: >-
            Speech voice, such as `alloy`, `ash`, `ballad`, `coral`, `echo`,
            `sage`, `shimmer` or `verse`. Music models ignore it.
        instructions:
          type: string
          description: >-
            How to deliver the text. By default the text is read verbatim. Music
            models ignore it.
        response_format:
          type: string
          default: wav
          description: >-
            `wav` (24 kHz mono) or `pcm16` (raw samples; `pcm` also works).
            Other values return WAV. Music models return their own container
            (MP3).
      additionalProperties: true
    GatewayError:
      type: object
      description: >-
        An error raised by pre.dev, in the OpenAI error shape with an extra
        `predev` object (absent on `catalog_unavailable` from the catalog
        listings). Branch on `error.code`.
      required:
        - error
      properties:
        error:
          type: object
          required:
            - message
            - type
            - code
            - param
          properties:
            message:
              type: string
              description: What went wrong and what to do next.
            type:
              type: string
              enum:
                - authentication_error
                - insufficient_credits
                - subscription_required
                - invalid_request_error
                - not_found_error
                - rate_limit_error
                - server_error
              description: >-
                Error class. The OpenAI and Anthropic SDKs map the HTTP status
                to their own exception types.
            code:
              type: string
              enum:
                - missing_api_key
                - invalid_api_key
                - too_many_auth_failures
                - insufficient_credits
                - subscription_required
                - rate_limit_exceeded
                - balance_unavailable
                - invalid_request
                - missing_id
                - not_found
                - not_ready
                - upstream_unavailable
                - catalog_unavailable
                - internal_error
              description: >-
                | Code | HTTP | Meaning |

                | --- | --- | --- |

                | `missing_api_key` | 401 | No `Authorization: Bearer` or
                `x-api-key` header. |

                | `invalid_api_key` | 401 | The key is unknown, or was rotated
                more than 15 minutes ago. |

                | `too_many_auth_failures` | 429 | More than 30 failed key
                attempts from this IP address in a minute. |

                | `insufficient_credits` | 402 | The balance is zero or below
                the estimate for this request. Nothing ran. |

                | `subscription_required` | 402 | A free workspace used its 5
                credits of AI calls. |

                | `rate_limit_exceeded` | 429 | Over the per-minute request
                limit for the workspace. |

                | `balance_unavailable` | 503 | The balance could not be read.
                Nothing ran. |

                | `invalid_request` | 400 | `/v1/audio/speech` is missing
                `model` or `input`, or the model does not produce audio. |

                | `missing_id` | 400 | `/v1/generation` was called without `id`.
                |

                | `not_found` | 404 | Unknown path, or a file, video job,
                response or generation that belongs to another workspace,
                including one a request refers to. On `/v1/generation`, also a
                call that recorded no stats (a failed call, for example) or a
                streamed call that is still running. |

                | `not_ready` | 404 or 502 | `/v1/generation` found the call,
                but its stats are not available yet (404) or could not be read
                (502). |

                | `upstream_unavailable` | 502 | The model did not answer, or
                returned no audio. |

                | `catalog_unavailable` | 502 or 503 | The model catalog could
                not be read: 502 from the catalog listings, 503 from `GET
                /v1/models/public`. Retry shortly. |

                | `internal_error` | 500 | Unexpected error. |
            param:
              type: 'null'
            predev:
              $ref: '#/components/schemas/GatewayErrorDetails'
    InvalidJsonBody:
      type: object
      description: The request body is not valid JSON. Checked before authentication.
      required:
        - error
      properties:
        error:
          type: string
          const: Invalid JSON body
    ModelError:
      type: object
      description: >-
        An error returned by the model for this request. The HTTP status is the
        model's own; `code` usually repeats it.
      required:
        - error
      properties:
        error:
          type: object
          required:
            - message
          properties:
            message:
              type: string
            code:
              type:
                - integer
                - string
            metadata:
              type: object
              properties:
                raw:
                  type: string
                  description: The model's own error text, when it adds detail.
                reasons:
                  type: array
                  items:
                    type: string
                  description: Moderation categories when the input was flagged.
                flagged_input:
                  type: string
                  description: The part of the input that was flagged.
    GatewayErrorDetails:
      type: object
      description: Extra fields for some error codes; an empty object for the rest.
      properties:
        credits_remaining:
          type: number
          description: >-
            Workspace balance. Sent with `insufficient_credits` and
            `subscription_required`.
        estimated_credits:
          type: number
          description: >-
            Estimated credits the request needs. Sent with
            `insufficient_credits`.
        trial_credits_used:
          type: number
          description: >-
            Credits the free trial has spent on AI calls. Sent with
            `subscription_required`.
        topup_url:
          type: string
          format: uri
          description: Where to add credits or subscribe.
        retry_after:
          type: integer
          description: Seconds to wait. Also sent as the `Retry-After` header.
        available_models:
          type: array
          items:
            type: string
          description: >-
            Speech and music model ids. Sent with `invalid_request` from
            `/v1/audio/speech`.
  headers:
    RequestId:
      description: >-
        pre.dev id for this request (`pdr_…`). Quote it when you contact
        support.
      schema:
        type: string
        example: pdr_043293a6-d1a8-4695-81fe-603ad4661ebb
    CreditsCharged:
      description: >-
        Credits charged for this call. Streamed responses do not carry it
        because the charge settles after the stream ends; read `GET
        /v1/generation` for those.
      schema:
        type: number
        example: 0.005
    CreditsRemaining:
      description: >-
        Workspace credit balance after this call. Omitted on plans without a
        credit limit.
      schema:
        type: number
        example: 412.5
    AudioFormat:
      description: >-
        Format of the audio body: `wav`, `pcm16;rate=24000;channels=1`, or the
        container a music model returned (`mp3`, `ogg`, `flac`).
      schema:
        type: string
        example: wav
    RetryAfter:
      description: Seconds to wait before retrying.
      schema:
        type: integer
        example: 12
  responses:
    Unauthorized:
      description: Missing or invalid API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/GatewayError'
          examples:
            missing_api_key:
              summary: No key sent
              value:
                error:
                  message: >-
                    Send your pre.dev API key as `Authorization: Bearer <key>`
                    or `x-api-key`. Copy it from Integrations → Built-in:
                    https://pre.dev/projects/integrations
                  type: authentication_error
                  code: missing_api_key
                  param: null
                  predev: {}
            invalid_api_key:
              summary: Unknown or retired key
              value:
                error:
                  message: Invalid API key
                  type: authentication_error
                  code: invalid_api_key
                  param: null
                  predev: {}
    PaymentRequired:
      description: >-
        The workspace cannot pay for this request. Nothing was sent to the model
        and nothing was charged.
      headers:
        x-predev-credits-remaining:
          $ref: '#/components/headers/CreditsRemaining'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/GatewayError'
          examples:
            no_credits:
              summary: Balance is zero
              value:
                error:
                  message: >-
                    This pre.dev workspace has no credits left for AI calls. Top
                    up at https://pre.dev/billing or enable auto-recharge.
                  type: insufficient_credits
                  code: insufficient_credits
                  param: null
                  predev:
                    credits_remaining: 0
                    estimated_credits: 0.05
                    topup_url: https://pre.dev/billing
            below_estimate:
              summary: Balance is below the estimate
              value:
                error:
                  message: >-
                    This request needs about 10.00 credits and the workspace has
                    3.20 left. Top up at https://pre.dev/billing.
                  type: insufficient_credits
                  code: insufficient_credits
                  param: null
                  predev:
                    credits_remaining: 3.2
                    estimated_credits: 10
                    topup_url: https://pre.dev/billing
            trial_used:
              summary: Free trial allowance used
              value:
                error:
                  message: >-
                    The free trial includes 5 credits of AI calls and this
                    workspace has used them. Subscribe at
                    https://pre.dev/billing to keep going.
                  type: subscription_required
                  code: subscription_required
                  param: null
                  predev:
                    credits_remaining: 14.2
                    trial_credits_used: 5.0132
                    topup_url: https://pre.dev/billing
    ModelNotFound:
      description: >-
        The request refers to a file, response or video job that is not this
        workspace's (`not_found` from pre.dev; nothing ran and nothing was
        charged), or the model could not serve the request (its own status and
        message, in the `ModelError` shape).
      content:
        application/json:
          schema:
            anyOf:
              - $ref: '#/components/schemas/GatewayError'
              - $ref: '#/components/schemas/ModelError'
          examples:
            foreign_reference:
              summary: A file that is not this workspace's
              value:
                error:
                  message: No file with id file-2Lm9Rt belongs to this workspace.
                  type: not_found_error
                  code: not_found
                  param: null
                  predev: {}
            model_not_found:
              summary: The model cannot serve the request
              value:
                error:
                  message: No endpoints found matching your data policy.
                  code: 404
    RateLimited:
      description: >-
        Too many requests. Wait `Retry-After` seconds when it is present. A
        `rate_limit_exceeded` refusal is per workspace per minute (600, or 30 on
        the free trial). An error from the model keeps the `ModelError` shape.
      headers:
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/json:
          schema:
            anyOf:
              - $ref: '#/components/schemas/GatewayError'
              - $ref: '#/components/schemas/ModelError'
          examples:
            rate_limit_exceeded:
              summary: Workspace rate limit
              value:
                error:
                  message: >-
                    Rate limit of 600 requests per minute exceeded for this
                    workspace.
                  type: rate_limit_error
                  code: rate_limit_exceeded
                  param: null
                  predev:
                    retry_after: 12
            too_many_auth_failures:
              summary: Failed key attempts from this IP
              value:
                error:
                  message: >-
                    Too many failed API key attempts from this address. Try
                    again in a minute.
                  type: rate_limit_error
                  code: too_many_auth_failures
                  param: null
                  predev:
                    retry_after: 60
            model_rate_limited:
              summary: The model is rate-limited
              value:
                error:
                  message: The model returned an error
                  code: 429
                  metadata:
                    raw: deepseek/deepseek-v4.1-flash is temporarily rate-limited.
    ServerError:
      description: Unexpected error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/GatewayError'
          example:
            error:
              message: The pre.dev AI gateway hit an unexpected error.
              type: server_error
              code: internal_error
              param: null
              predev: {}
    BalanceUnavailable:
      description: >-
        The credit balance could not be read, so nothing ran. Retry after
        `Retry-After` seconds.
      headers:
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/GatewayError'
          example:
            error:
              message: Could not read this workspace's credit balance. Retry shortly.
              type: server_error
              code: balance_unavailable
              param: null
              predev:
                retry_after: 5
  securitySchemes:
    apiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: pdk_ API key
      description: >-
        The workspace API key (`pdk_…`) from Integrations → Built-in in the
        pre.dev dashboard.
    xApiKey:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        Alternative to `Authorization: Bearer`. The Anthropic SDKs send this
        header.

````

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