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

# Get a signed URL for uploading a recording

> Generate signed upload and download URLs for an audio file. The upload URL can be used to upload the file directly to cloud storage.

Generates a short-lived **signed upload URL** for an audio file plus a matching **download URL**. This is the first step of the direct-upload flow: upload your audio to cloud storage with `uploadUrl`, then pass `downloadUrl` as `fileUrl` to [Post recordings upload](/api-references/post-recordings-upload) to create the recording.

## Response fields

| Field           | Description                                                      |
| --------------- | ---------------------------------------------------------------- |
| `uploadUrl`     | Signed URL to **`PUT`** the file body to (Google Cloud Storage). |
| `uploadHeaders` | Headers you **must** send with the `PUT` — see below.            |
| `downloadUrl`   | Pass this as `fileUrl` when creating the recording.              |
| `expiresAt`     | When the signed URLs expire (ISO 8601). Upload before this time. |

## Required upload headers

The values of `Content-Type` and `x-goog-content-length-range` are **baked into the signature** of `uploadUrl`. You must send them **exactly** as returned in `uploadHeaders`, or GCS rejects the upload with a `403 SignatureDoesNotMatch`.

* **`Content-Type`** — the MIME type for the `fileType` you requested. Siro maps extensions as follows:

  | `fileType` | `Content-Type` |
  | ---------- | -------------- |
  | `mp3`      | `audio/mpeg`   |
  | `wav`      | `audio/wav`    |
  | `aac`      | `audio/aac`    |

* **`x-goog-content-length-range`** — `0,<fileSize>`, where `<fileSize>` is the exact byte size you passed in the request. The uploaded body must fall within this range.

## Example upload

```bash theme={null}
# 1. Request the signed URLs
curl -X POST https://api.siro.ai/v1/core/recordings/signed-urls \
  -H "x-siro-auth-token: $SIRO_OAUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"fileType": "mp3", "fileSize": 1048576}'

# Response:
# {
#   "data": {
#     "uploadUrl": "https://storage.googleapis.com/...",
#     "uploadHeaders": {
#       "Content-Type": "audio/mpeg",
#       "x-goog-content-length-range": "0,1048576"
#     },
#     "downloadUrl": "https://storage.googleapis.com/...",
#     "expiresAt": "2026-06-08T12:00:00.000Z"
#   }
# }

# 2. PUT the file to uploadUrl, echoing uploadHeaders verbatim
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: audio/mpeg" \
  -H "x-goog-content-length-range: 0,1048576" \
  --data-binary @recording.mp3
```

Then call [Post recordings upload](/api-references/post-recordings-upload) with `fileUrl` set to `downloadUrl`.


## OpenAPI

````yaml post /v1/core/recordings/signed-urls
openapi: 3.0.0
info:
  version: 1.0.0
  title: Recordings API
servers:
  - url: https://api.siro.ai/
    description: Siro API Gateway
security: []
externalDocs:
  description: View the raw OpenAPI Specification in JSON format
  url: /openapi.json
paths:
  /v1/core/recordings/signed-urls:
    post:
      summary: Get a signed URL for uploading a recording
      description: >-
        Generate signed upload and download URLs for an audio file. The upload
        URL can be used to upload the file directly to cloud storage.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                fileType:
                  type: string
                  description: File type (limited to mp3, aac, wav)
                fileSize:
                  type: integer
                  minimum: 0
                  exclusiveMinimum: true
                  description: Size of the file in bytes
              required:
                - fileType
                - fileSize
              additionalProperties: false
      responses:
        '200':
          description: Get a signed URL for uploading a recording
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      uploadUrl:
                        type: string
                        description: Signed URL for uploading the file (PUT the body here).
                      uploadHeaders:
                        type: object
                        properties:
                          Content-Type:
                            type: string
                            description: >-
                              MIME type for the file. Must be sent exactly when
                              PUTting to `uploadUrl` (it is baked into the
                              signature).
                          x-goog-content-length-range:
                            type: string
                            description: >-
                              Allowed upload size range `0,<fileSize>`. Must be
                              sent exactly when PUTting to `uploadUrl` (it is
                              baked into the signature).
                        required:
                          - Content-Type
                          - x-goog-content-length-range
                        description: >-
                          Headers that must be included exactly as given when
                          PUTting the file to `uploadUrl`.
                      downloadUrl:
                        type: string
                        description: >-
                          Pass this URL as `fileUrl` when creating the
                          recording.
                      expiresAt:
                        type: string
                        description: When the signed URLs expire (ISO 8601 date-time).
                    required:
                      - uploadUrl
                      - uploadHeaders
                      - downloadUrl
                      - expiresAt
                  cursor:
                    anyOf:
                      - type: string
                      - type: number
                      - nullable: true
                  pageSize:
                    type: number
                  limit:
                    type: number
                  total:
                    type: number
                    nullable: true
                  hasNextPage:
                    type: boolean
                required:
                  - data
        '400':
          description: Bad Request - Invalid parameters
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: Error message
                required:
                  - error
        '401':
          description: Unauthorized - User not authenticated
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: Authentication error message
                required:
                  - error
        '403':
          description: Forbidden - User does not have access
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: Authorization error message
                required:
                  - error
        '404':
          description: Not Found - Resource not found
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: Resource not found error message
                required:
                  - error
        '422':
          description: Unprocessable Content - The request failed validation checks
          content:
            application/json:
              schema:
                type: object
                properties:
                  issues:
                    type: array
                    items:
                      type: object
                      properties:
                        code:
                          type: string
                        expected:
                          type: string
                        received:
                          type: string
                        path:
                          type: array
                          items:
                            anyOf:
                              - type: string
                              - type: number
                        message:
                          type: string
                      required:
                        - code
                        - path
                        - message
                  name:
                    type: string
                    enum:
                      - ZodError
                required:
                  - issues
                  - name
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: Internal server error message
                required:
                  - error
      security:
        - SiroAuthToken: []
components:
  securitySchemes:
    SiroAuthToken:
      type: apiKey
      in: header
      name: x-siro-auth-token
      description: >-
        OAuth access token for user-scoped requests (Authorization Code or
        machine-to-machine). Send header `x-siro-auth-token:
        <oauth-access-token>`. Not an organization API key.
      x-default: <oauth-access-token>

````