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

# Generate Upload URL

> Generate a presigned URL for direct upload of signed release forms to cloud storage

# Generate Release Form Upload URL

Generates a presigned URL that allows direct upload of signed release forms to cloud storage. This enables clients to upload release form PDFs directly without proxying through the API server.

## Authentication

**Required** - Bearer token authentication. The tenant context is extracted from the JWT token.

## Response

<ResponseField name="signedUrl" type="string">
  A presigned URL valid for 15 minutes that allows direct PUT upload to cloud storage
</ResponseField>

<ResponseField name="fileUri" type="string">
  The GCS URI (`gs://bucket/path`) where the file will be stored. Use this value as `signedReleaseFileUrl` when creating verification orders. Alternatively, you can pass an HTTPS URL or inline base64 PDF directly in `signedReleaseFileUrl` — see [Signed release file](/api-reference/schemas/applicant#signed-release-file).
</ResponseField>

## Example Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -sS -X GET "https://sandbox.theary.ai/files/release-form/generate-upload-url" \
    -H "Authorization: Bearer YOUR_JWT_TOKEN" \
    -H "Accept: application/json"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://sandbox.theary.ai/files/release-form/generate-upload-url', {
    headers: {
      'Authorization': 'Bearer YOUR_JWT_TOKEN'
    }
  })
  const { signedUrl, fileUri } = await response.json()
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      'https://sandbox.theary.ai/files/release-form/generate-upload-url',
      headers={'Authorization': 'Bearer YOUR_JWT_TOKEN'}
  )
  data = response.json()
  signed_url = data['signedUrl']
  file_uri = data['fileUri']
  ```
</CodeGroup>

## Example Response

```json theme={null}
{
  "signedUrl": "https://storage.googleapis.com/verification-api-storage/tenants/acme-corp/releaseForms/550e8400-e29b-41d4-a716-446655440000.pdf?X-Goog-Algorithm=...",
  "fileUri": "gs://verification-api-storage/tenants/acme-corp/releaseForms/550e8400-e29b-41d4-a716-446655440000.pdf"
}
```

## Uploading the File

After obtaining the signed URL, upload your PDF file directly to cloud storage:

<CodeGroup>
  ```bash cURL theme={null}
  # Upload the release form PDF
  curl -sS -X PUT "${SIGNED_URL}" \
    -H "Content-Type: application/pdf" \
    --data-binary @signed-release-form.pdf
  ```

  ```javascript JavaScript theme={null}
  // Upload the release form PDF
  const fileBuffer = await fs.promises.readFile('signed-release-form.pdf')
  await fetch(signedUrl, {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/pdf'
    },
    body: fileBuffer
  })
  ```

  ```python Python theme={null}
  # Upload the release form PDF
  with open('signed-release-form.pdf', 'rb') as f:
      requests.put(
          signed_url,
          headers={'Content-Type': 'application/pdf'},
          data=f.read()
      )
  ```
</CodeGroup>

## Using the File URI

After uploading, use the `fileUri` as the `signedReleaseFileUrl` when creating a verification order:

```json theme={null}
{
  "applicant": {
    "firstName": "John",
    "lastName": "Smith",
    "ssn": "123-45-6789",
    "signedReleaseFileUrl": "gs://verification-api-storage/tenants/acme-corp/releaseForms/550e8400-e29b-41d4-a716-446655440000.pdf"
  },
  // ... rest of order
}
```

## File Storage

Files are organized by tenant for security and isolation:

```
gs://verification-api-storage/
  └── tenants/
      └── {tenant-name}/
          └── releaseForms/
              └── {uuid}.pdf
```

## Important Notes

* **URL Expiration**: The signed URL expires after 15 minutes
* **File Type**: Only PDF files are supported (`application/pdf`)
* **File Size**: There is currently no server-side size limit enforced on files uploaded through this signed-URL flow. As a best practice, keep uploads reasonably sized (a signed release form is typically well under 1MB). The **10MB cap** documented for `signedReleaseFileUrl` applies only to the separate inline base64 upload path used by [Create order](/api-reference/endpoints/create-order) — it does not apply here.
* **Tenant Isolation**: Files are stored in tenant-specific directories
* **Security**: Files can only be accessed by the owning tenant

## Response Codes

| Status Code | Description                                 |
| ----------- | ------------------------------------------- |
| `200`       | Upload URL generated successfully           |
| `401`       | Unauthorized - invalid or missing JWT token |
| `500`       | Internal server error                       |

## Error Responses

<ResponseExample>
  ```json 401 Unauthorized theme={null}
  {
    "statusCode": 401,
    "message": "Unauthorized"
  }
  ```
</ResponseExample>

<ResponseExample>
  ```json 500 Internal Server Error theme={null}
  {
    "statusCode": 500,
    "message": "Failed to generate upload URL",
    "error": "Internal Server Error"
  }
  ```
</ResponseExample>


## OpenAPI

````yaml GET /files/release-form/generate-upload-url
openapi: 3.0.0
info:
  title: SNH AI Background Check API
  description: >-
    AI-powered agentic background verification service for employment and
    education checks.
  version: '1.0'
  contact: {}
servers:
  - url: https://sandbox.theary.ai
    description: Sandbox
  - url: https://api.theary.ai
    description: Production
  - url: http://localhost:3000
    description: Local development
security:
  - bearerAuth: []
tags: []
paths:
  /files/release-form/generate-upload-url:
    get:
      tags:
        - Files
      summary: Generate signed URL for release form upload
      description: >-
        Returns a presigned URL for direct upload of signed release forms to
        cloud storage
      operationId: get-release-form-upload-url
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: object
      security:
        - bearerAuth: []
components:
  securitySchemes:
    bearerAuth:
      scheme: bearer
      bearerFormat: JWT
      type: http

````