# Partner CSV Upload API

## Overview

The Partner CSV Upload API lets you securely upload CSV files to Intelibly for storage and later
use. Uploads are associated automatically with your organization based on your API credentials.

- **Base URL (production):** `https://app.intelibly.com`
- **Base URL (staging/dev):** provided separately for your environment

## Typical integration flow

```text
1. POST /api/token
      → access_token

2. (Optional) GET /api/csv_uploads/template
      → header row for file generation

3. Generate your CSV using the recommended columns

4. POST /api/csv_uploads
      → multipart file upload
      → receive upload id

5. (Optional) GET /api/csv_uploads
      → confirm the file appears in your upload history

6. (Optional) GET /api/csv_uploads/{id}/download
      → verify stored contents via temporary URL

7. Refresh the access token before it expires (step 1)
```

## Authentication

All endpoints require a Bearer access token obtained with the OAuth 2.0 **client credentials**
grant.

### Credentials

Intelibly will provide:

| Credential | Description |
| --- | --- |
| `client_id` | Public identifier for your API client |
| `client_secret` | Secret used to obtain access tokens (keep confidential) |

### Obtain an access token

```http
POST /api/token
Content-Type: application/json
```

**Request body**

```json
{
  "client_id": "YOUR_CLIENT_ID",
  "client_secret": "YOUR_CLIENT_SECRET",
  "grant_type": "client_credentials"
}
```

**Successful response** `200 OK`

```json
{
  "access_token": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600
}
```

| Field | Description |
| --- | --- |
| `access_token` | JWT to send on subsequent requests |
| `token_type` | Always `Bearer` |
| `expires_in` | Token lifetime in seconds (typically **3600** / 1 hour) |

**Error response** `400 Bad Request`

```json
{
  "error": "invalid_client"
}
```

or

```json
{
  "error": "invalid_request"
}
```

### Using the access token

Include the token on every protected request:

```http
Authorization: Bearer YOUR_ACCESS_TOKEN
```

Tokens expire after approximately one hour. Request a new token when `expires_in` elapses; do not
reuse expired tokens.

## Endpoints

### 1. Upload a CSV file

```http
POST /api/csv_uploads
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: multipart/form-data
```

**Form fields**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `file` | file | Yes | CSV file to upload |

**Constraints**

| Rule | Value |
| --- | --- |
| File extension | `.csv` only |
| Maximum file size | **200 MB** (209,715,200 bytes) |
| Content type | `text/csv` |
| Files per request | 1 |

**Example (cURL)**

```bash
curl -X POST "https://app.intelibly.com/api/csv_uploads" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -F "file=@./practices.csv"
```

**Successful response** `201 Created`

```json
{
  "data": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "filename": "practices.csv",
    "file_size": 24576,
    "content_type": "text/csv",
    "inserted_at": "2026-07-21T18:30:00.000000Z"
  }
}
```

**Error responses**

| Status | Condition |
| --- | --- |
| `400` / `422` | Missing file, non-`.csv` filename, empty file, or file too large |
| `401` | Missing or invalid access token |

Example validation error:

```json
{
  "errors": {
    "filename": ["must be a CSV file"]
  }
}
```

### 2. List uploaded files

Returns your uploaded files, newest first.

```http
GET /api/csv_uploads
Authorization: Bearer YOUR_ACCESS_TOKEN
```

**Successful response** `200 OK`

```json
{
  "data": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "filename": "practices.csv",
      "file_size": 24576,
      "content_type": "text/csv",
      "inserted_at": "2026-07-21T18:30:00.000000Z"
    }
  ]
}
```

### 3. Get download URL for an uploaded file

Returns a time-limited URL to download a previously uploaded file.

```http
GET /api/csv_uploads/:id/download
Authorization: Bearer YOUR_ACCESS_TOKEN
```

**Path parameters**

| Parameter | Type | Description |
| --- | --- | --- |
| `id` | UUID | Upload ID from the list or upload response |

**Successful response** `200 OK`

```json
{
  "data": {
    "url": "https://...",
    "expires_in": 3600,
    "filename": "practices.csv"
  }
}
```

| Field | Description |
| --- | --- |
| `url` | Temporary HTTPS URL for a `GET` download |
| `expires_in` | URL lifetime in seconds (typically **3600**) |
| `filename` | Original filename |

Use the returned `url` with a normal HTTP GET. No API Bearer token is required on the download URL
itself. Download the file before the URL expires.

**Error responses**

| Status | Condition |
| --- | --- |
| `401` | Missing or invalid access token |
| `404` | Upload not found |

### 4. Delete an uploaded file

Permanently removes an uploaded file.

```http
DELETE /api/csv_uploads/:id
Authorization: Bearer YOUR_ACCESS_TOKEN
```

**Successful response** `204 No Content`

**Error responses**

| Status | Condition |
| --- | --- |
| `401` | Missing or invalid access token |
| `404` | Upload not found |

### 5. Download CSV header template

Returns a CSV containing only the recommended header row. Useful when generating files
programmatically.

```http
GET /api/csv_uploads/template
Authorization: Bearer YOUR_ACCESS_TOKEN
```

**Successful response** `200 OK`

```http
Content-Type: text/csv
Content-Disposition: attachment; filename="partner_import.csv"
```

The response body is a single header row (see Recommended CSV format below).

## Recommended CSV format

For practice and provider data, use the following header row (case-insensitive):

```text
practice_name,practice_email,practice_website,practice_contact_name,location_name,location_phone,location_street,location_city,location_state,location_zip,location_internal_id,provider_name,provider_specialty,provider_internal_id,partner_tier_name,partners_internal_id,npi_number,partner_tags
```

### Column reference

| Column | Required | Description |
| --- | --- | --- |
| `practice_name` | Yes | Practice / organization name |
| `practice_email` | Yes | Practice contact email |
| `practice_website` | No | Practice website URL |
| `practice_contact_name` | Yes | Primary contact name |
| `location_name` | No | Location / office name |
| `location_phone` | Yes | Location phone number |
| `location_street` | Yes | Street address |
| `location_city` | Yes | City |
| `location_state` | Yes | State / province |
| `location_zip` | Yes | Postal / ZIP code |
| `location_internal_id` | Conditional\* | Your internal location ID |
| `provider_name` | Yes | Provider full name |
| `provider_specialty` | No | Specialty; blank or invalid values default to **General Dentist** |
| `provider_internal_id` | Conditional\* | Your internal provider ID |
| `partner_tier_name` | No | Partner tier label (e.g. `gold`) |
| `partners_internal_id` | No | Additional partner-side identifier |
| `npi_number` | No | National Provider Identifier |
| `partner_tags` | No | Comma-separated tags within the cell (quote the field if needed) |

\*Your Intelibly contact will confirm if internal location and provider IDs are required for your
integration. The [CSV Template Field Reference](/partner-api/csv-template) describes every field in
detail, including the requirements for FinderCONNECT Button and FinderCONNECT Map.

### Example row

```csv
practice_name,practice_email,practice_website,practice_contact_name,location_name,location_phone,location_street,location_city,location_state,location_zip,location_internal_id,provider_name,provider_specialty,provider_internal_id,partner_tier_name,partners_internal_id,npi_number,partner_tags
Sample Family Dental,frontdesk@example.com,www.example.com,Jordan Sample,Main Office,(210) 555-0123,100 Example St., San Antonio, TX 78229,,Dr. Alex Sample,Periodontics,,gold,,1234567893,Product Name
```

## Error handling summary

| HTTP status | Meaning |
| --- | --- |
| `200` | Success (list, download URL, template) |
| `201` | File created |
| `204` | File deleted |
| `400` | Bad request (e.g. invalid token request, malformed upload) |
| `401` | Authentication required or token invalid/expired |
| `404` | Resource not found |
| `422` | Validation failed (filename, size, etc.) |
| `500` | Unexpected server error |

Prefer checking HTTP status codes. Where present, read `errors.detail` or field-level `errors` for
human-readable messages.

## Security notes

- Store `client_secret` securely (environment variables / secrets manager). Never embed it in
  client-side applications or public repositories.
- Transmit credentials and tokens only over HTTPS.
- Use access tokens only for server-to-server automation; rotate credentials if compromised.
- Download URLs are short-lived; treat them as sensitive while valid.

## Support

For credential provisioning or questions about the recommended CSV format, contact your Intelibly
Provider Relations representative.
