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

# File Detection & Confirmation

> Automatically detect royalty sources with confidence scoring and smart file recognition

## Overview

Upload royalty files and let the API automatically detect the source (Spotify, Apple Music, etc.), accounting period, and file schema. The system uses intelligent pattern matching to identify files and provides confidence scores so you know when to review manually versus auto-process.

### Key Features

* **Automatic source detection** from filename and content patterns
* **Confidence scoring** (0-1 scale) for detection accuracy
* **Period extraction** from filenames like `spotify_2024-01.csv`
* **Manual confirmation** workflow for low-confidence matches
* **Auto-processing** for trusted sources with high confidence
* **Real-time status polling** to track detection progress

***

## Prerequisites

### Authentication

All requests require a valid API token:

<CodeGroup>
  ```javascript Node.js theme={null}
  const headers = {
    'Authorization': `Bearer ${YOUR_API_TOKEN}`,
    'Content-Type': 'application/json'
  };
  ```

  ```python Python theme={null}
  headers = {
      'Authorization': f'Bearer {YOUR_API_TOKEN}',
      'Content-Type': 'application/json'
  }
  ```
</CodeGroup>

***

## Quick Start

<Steps>
  <Step title="Request Upload URL">
    Get a pre-signed URL for uploading your file with detection enabled.

    ```javascript Node.js theme={null}
    const response = await fetch('https://api.royalti.io/file/upload-url', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${YOUR_API_TOKEN}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        fileName: 'spotify_2024-01.csv',
        fileSize: 1048576,
        uploadType: 'royalty',
        enableDetection: true  // Enable automatic detection
      })
    });

    const { signedUrl, sessionId, fileId } = await response.json();
    ```

    Save the `sessionId` - you'll need it to check detection status.
  </Step>

  <Step title="Upload File">
    Upload your file to the signed URL using a PUT request.

    ```javascript Node.js theme={null}
    const file = fs.readFileSync('./spotify_2024-01.csv');

    await fetch(signedUrl, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/octet-stream' },
      body: file
    });
    ```

    The detection process starts automatically after upload completes.
  </Step>

  <Step title="Check Detection Status">
    Poll the detection endpoint to get results.

    ```javascript Node.js theme={null}
    const detectionResponse = await fetch(
      `https://api.royalti.io/royalty/detection/${sessionId}`,
      { headers: { 'Authorization': `Bearer ${YOUR_API_TOKEN}` } }
    );

    const detection = await detectionResponse.json();

    console.log('Detected source:', detection.data.detectedValues.source);
    console.log('Confidence:', detection.data.confidence.source);
    // Output: Detected source: spotify
    // Output: Confidence: 0.95
    ```
  </Step>
</Steps>

<Note>
  Files with confidence scores above 0.8 can be auto-processed if you enable that feature. See [Auto-Processing Configuration](#auto-processing) below.
</Note>

***

## Understanding Confidence Scores

The API returns three confidence scores for each file:

| Score Type | What It Measures                     | Threshold for Auto-Process |
| ---------- | ------------------------------------ | -------------------------- |
| **source** | How certain the source detection is  | > 0.8                      |
| **period** | How certain the period extraction is | > 0.7                      |
| **schema** | How well the file structure matches  | > 0.8                      |

### Confidence Levels

<Tabs>
  <Tab title="High Confidence (0.8-1.0)">
    **What it means:** Detection is highly reliable

    **Action:** File can be auto-processed

    **Example:**

    ```json theme={null}
    {
      "confidence": {
        "source": 0.95,
        "period": 0.89,
        "schema": 0.92
      }
    }
    ```
  </Tab>

  <Tab title="Medium Confidence (0.6-0.79)">
    **What it means:** Detection is likely correct but review recommended

    **Action:** Manual confirmation required

    **Example:**

    ```json theme={null}
    {
      "confidence": {
        "source": 0.75,
        "period": 0.68,
        "schema": 0.71
      }
    }
    ```
  </Tab>

  <Tab title="Low Confidence (0.0-0.59)">
    **What it means:** Detection uncertain, may be incorrect

    **Action:** Manual review strongly recommended

    **Example:**

    ```json theme={null}
    {
      "confidence": {
        "source": 0.45,
        "period": 0.52,
        "schema": 0.48
      }
    }
    ```
  </Tab>
</Tabs>

***

## Dual-Period Sources

Some royalty sources (like Merlin aggregator sources) require **both** a sale period and an accounting period. These are called "dual-period sources."

### How to Identify Dual-Period Sources

When you upload a file, check the detection response for `requiresAccountingPeriod`:

```json theme={null}
{
  "data": {
    "detectedValues": {
      "source": "merlin_snap",
      "detectedSalePeriod": "2024-06-01",
      "requiresAccountingPeriod": true
    }
  }
}
```

<Note>
  When `requiresAccountingPeriod: true`, you **must** provide both `accountingPeriod` and `salePeriod` when confirming. The system will block auto-processing until both periods are provided.
</Note>

### Common Dual-Period Sources

| Source            | Table Format                    | Example Table Name              |
| ----------------- | ------------------------------- | ------------------------------- |
| `merlin_snap`     | `merlin_snap_SALPER_ACCPER`     | `merlin_snap_202406_202406`     |
| `merlin_facebook` | `merlin_facebook_SALPER_ACCPER` | `merlin_facebook_202406_202407` |
| `merlin_tiktok`   | `merlin_tiktok_SALPER_ACCPER`   | `merlin_tiktok_202406_202406`   |

### Understanding Sale Period vs Accounting Period

| Period Type           | Description                           | Source                                                |
| --------------------- | ------------------------------------- | ----------------------------------------------------- |
| **Sale Period**       | When the streams/sales occurred       | Usually from filename (e.g., `_202406_`)              |
| **Accounting Period** | When the royalties are being reported | Usually from file content (e.g., `Start_Date` column) |

<Tip>
  For most files, these periods are the same. But some distributors report sales with a delay, so the accounting period may be different from the sale period.
</Tip>

***

## Detection Workflow

### Polling for Detection Results

Detection typically completes within 2-10 seconds. Poll every 2 seconds until status is `detected`:

<CodeGroup>
  ```javascript Node.js theme={null}
  async function waitForDetection(sessionId) {
    for (let attempt = 0; attempt < 30; attempt++) {
      const response = await fetch(
        `https://api.royalti.io/royalty/detection/${sessionId}`,
        { headers: { 'Authorization': `Bearer ${YOUR_API_TOKEN}` } }
      );

      const detection = await response.json();
      const status = detection.data.status;

      if (status === 'detected' || status === 'auto_confirmed') {
        return detection.data;
      }

      if (status === 'expired') {
        throw new Error('Detection session expired after 24 hours');
      }

      // Wait 2 seconds before next attempt
      await new Promise(resolve => setTimeout(resolve, 2000));
    }

    throw new Error('Detection timeout after 60 seconds');
  }

  // Usage
  const result = await waitForDetection(sessionId);
  console.log('Detection complete:', result);
  ```

  ```python Python theme={null}
  import time

  def wait_for_detection(session_id: str):
      for attempt in range(30):
          response = requests.get(
              f'https://api.royalti.io/royalty/detection/{session_id}',
              headers={'Authorization': f'Bearer {YOUR_API_TOKEN}'}
          )

          detection = response.json()
          status = detection['data']['status']

          if status in ['detected', 'auto_confirmed']:
              return detection['data']

          if status == 'expired':
              raise Exception('Detection session expired after 24 hours')

          # Wait 2 seconds before next attempt
          time.sleep(2)

      raise Exception('Detection timeout after 60 seconds')

  # Usage
  result = wait_for_detection(session_id)
  print('Detection complete:', result)
  ```
</CodeGroup>

### Detection Response Format

```json theme={null}
{
  "status": "success",
  "data": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "status": "detected",
    "fileName": "merlin_snap_202406_Monthly-Sales.csv",
    "fileSize": 1048576,
    "detectedValues": {
      "source": "merlin_snap",
      "period": "2024-06-01",
      "schema": "Service:STRING,Start_Date:DATE,...",
      "detectedSalePeriod": "2024-06-01",
      "detectedAccountingPeriod": null,
      "requiresAccountingPeriod": true,
      "suggestedPeriodMapping": "salePeriod",
      "periodSource": "filename"
    },
    "confidence": {
      "source": 1.0,
      "period": 0.85,
      "schema": 1.0
    },
    "completedSteps": ["upload", "analysis", "pattern_matching"],
    "expiresAt": "2025-01-16T12:00:00Z"
  }
}
```

### Key Response Fields

| Field                      | Description                                                          |
| -------------------------- | -------------------------------------------------------------------- |
| `detectedSalePeriod`       | Detected sale period (from filename or content)                      |
| `detectedAccountingPeriod` | Detected accounting period (if found in content)                     |
| `requiresAccountingPeriod` | `true` if this source needs both periods                             |
| `suggestedPeriodMapping`   | Whether detected period should be `salePeriod` or `accountingPeriod` |
| `periodSource`             | Where period was detected: `filename` or `content`                   |

***

## Confirming Detection

### When to Confirm Manually

Manual confirmation is required when:

* Any confidence score \< 0.8
* Auto-processing is disabled
* You want to review before processing
* Detection identified an unexpected source
* **Dual-period sources** require both `accountingPeriod` and `salePeriod`

### Confirmation Endpoints

There are two endpoints for confirming detection:

| Endpoint                                  | Use Case                     | Source Input             |
| ----------------------------------------- | ---------------------------- | ------------------------ |
| `POST /file/detection/:sessionId`         | Quick confirmation with UUID | `royaltySourceId` (UUID) |
| `POST /file/confirm-detection/:sessionId` | Full flow with learning      | `royaltySource` (name)   |

### Option 1: Quick Confirmation (by Source ID)

Use this when you have the source UUID and want simple confirmation.

<CodeGroup>
  ```javascript Node.js theme={null}
  async function confirmDetection(sessionId, sourceId, accountingPeriod, salePeriod) {
    const response = await fetch(
      `https://api.royalti.io/file/detection/${sessionId}`,
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${YOUR_API_TOKEN}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          royaltySourceId: sourceId,
          accountingPeriod: accountingPeriod,  // Format: YYYY-MM-DD
          salePeriod: salePeriod,              // For dual-period sources
          confirm: true                         // Required to trigger processing
        })
      }
    );

    const result = await response.json();
    return result.data.jobId;
  }

  // Usage - Single period source
  const jobId = await confirmDetection(
    sessionId,
    '456e7890-e89b-12d3-a456-426614174001',
    '2024-01-01',
    null
  );

  // Usage - Dual period source (e.g., Merlin sources)
  const jobId = await confirmDetection(
    sessionId,
    '59339018-d21e-4882-b81a-2d5bce37600e',
    '2024-06-01',  // Accounting period
    '2024-06-01'   // Sale period
  );

  console.log('Processing started. Job ID:', jobId);
  ```

  ```python Python theme={null}
  def confirm_detection(session_id: str, source_id: str,
                        accounting_period: str, sale_period: str = None):
      response = requests.post(
          f'https://api.royalti.io/file/detection/{session_id}',
          headers={
              'Authorization': f'Bearer {YOUR_API_TOKEN}',
              'Content-Type': 'application/json'
          },
          json={
              'royaltySourceId': source_id,
              'accountingPeriod': accounting_period,  # Format: YYYY-MM-DD
              'salePeriod': sale_period,               # For dual-period sources
              'confirm': True                          # Required to trigger processing
          }
      )

      result = response.json()
      return result['data']['jobId']

  # Usage - Single period source
  job_id = confirm_detection(
      session_id,
      '456e7890-e89b-12d3-a456-426614174001',
      '2024-01-01'
  )

  # Usage - Dual period source
  job_id = confirm_detection(
      session_id,
      '59339018-d21e-4882-b81a-2d5bce37600e',
      '2024-06-01',  # Accounting period
      '2024-06-01'   # Sale period
  )

  print(f'Processing started. Job ID: {job_id}')
  ```

  ```bash cURL theme={null}
  curl -X POST \
    https://api.royalti.io/file/detection/123e4567-e89b-12d3-a456-426614174000 \
    -H "Authorization: Bearer YOUR_API_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "royaltySourceId": "456e7890-e89b-12d3-a456-426614174001",
      "accountingPeriod": "2024-01-01",
      "salePeriod": "2024-01-01",
      "confirm": true
    }'
  ```
</CodeGroup>

<Warning>
  The `confirm: true` flag is **required** to trigger processing. Without it, the file is marked for manual review but not processed.
</Warning>

### Option 2: Full Confirmation (by Source Name)

Use this for the complete workflow with correction tracking and learning.

<CodeGroup>
  ```javascript Node.js theme={null}
  async function confirmDetectionFull(sessionId, sourceName, accountingPeriod, salePeriod) {
    const response = await fetch(
      `https://api.royalti.io/file/confirm-detection/${sessionId}`,
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${YOUR_API_TOKEN}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          royaltySource: sourceName,           // Source NAME, not UUID
          accountingPeriod: accountingPeriod,
          salePeriod: salePeriod               // For dual-period sources
        })
      }
    );

    const result = await response.json();
    return result.data;
  }

  // Usage
  const result = await confirmDetectionFull(
    sessionId,
    'merlin_snap',     // Source name
    '2024-06-01',      // Accounting period
    '2024-06-01'       // Sale period
  );
  ```

  ```bash cURL theme={null}
  curl -X POST \
    https://api.royalti.io/file/confirm-detection/123e4567-e89b-12d3-a456-426614174000 \
    -H "Authorization: Bearer YOUR_API_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "royaltySource": "merlin_snap",
      "accountingPeriod": "2024-06-01",
      "salePeriod": "2024-06-01"
    }'
  ```
</CodeGroup>

This endpoint also:

* Records corrections if you change detected values (for learning)
* Tracks period mapping corrections (salePeriod vs accountingPeriod)
* Updates cloud storage metadata
* Supports ZIP file confirmations

### Getting Source ID

You need the source ID to confirm detection. Retrieve it from the sources endpoint:

```javascript Node.js theme={null}
const sourcesResponse = await fetch(
  'https://api.royalti.io/royalty/sources',
  { headers: { 'Authorization': `Bearer ${YOUR_API_TOKEN}` } }
);

const sources = await sourcesResponse.json();
const spotify = sources.data.sources.find(s => s.name === 'spotify');
const sourceId = spotify.id;
```

***

## Monitoring Processing

After confirmation, track processing progress:

<CodeGroup>
  ```javascript Node.js theme={null}
  async function checkProcessingStatus(jobId) {
    const response = await fetch(
      `https://api.royalti.io/royalty/processing/${jobId}`,
      { headers: { 'Authorization': `Bearer ${YOUR_API_TOKEN}` } }
    );

    const result = await response.json();
    const job = result.data;

    console.log(`Status: ${job.status}`);
    console.log(`Progress: ${job.progress}%`);
    console.log(`Rows processed: ${job.rowsProcessed}/${job.rowsTotal}`);

    return job;
  }

  // Poll every 3 seconds
  async function waitForProcessing(jobId) {
    while (true) {
      const job = await checkProcessingStatus(jobId);

      if (job.status === 'completed') {
        console.log('✓ Processing complete!');
        return job;
      }

      if (job.status === 'failed') {
        console.error('✗ Processing failed:', job.errors);
        throw new Error('Processing failed');
      }

      await new Promise(resolve => setTimeout(resolve, 3000));
    }
  }
  ```

  ```python Python theme={null}
  def check_processing_status(job_id: str):
      response = requests.get(
          f'https://api.royalti.io/royalty/processing/{job_id}',
          headers={'Authorization': f'Bearer {YOUR_API_TOKEN}'}
      )

      result = response.json()
      job = result['data']

      print(f"Status: {job['status']}")
      print(f"Progress: {job['progress']}%")
      print(f"Rows processed: {job['rowsProcessed']}/{job['rowsTotal']}")

      return job

  # Poll every 3 seconds
  def wait_for_processing(job_id: str):
      while True:
          job = check_processing_status(job_id)

          if job['status'] == 'completed':
              print('✓ Processing complete!')
              return job

          if job['status'] == 'failed':
              print(f"✗ Processing failed: {job['errors']}")
              raise Exception('Processing failed')

          time.sleep(3)
  ```
</CodeGroup>

### Processing Status Values

| Status      | Description                     |
| ----------- | ------------------------------- |
| `waiting`   | Job is queued, waiting to start |
| `active`    | Currently processing rows       |
| `completed` | Successfully finished           |
| `failed`    | Processing encountered an error |
| `delayed`   | Temporarily delayed, will retry |

***

## Auto-Processing

Enable auto-processing to automatically process files with high confidence scores.

### Enable Auto-Processing

<CodeGroup>
  ```javascript Node.js theme={null}
  const response = await fetch(
    'https://api.royalti.io/file/enable-auto-processing',
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${YOUR_API_TOKEN}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        enabled: true,
        confidenceThresholds: {
          source: 0.85,   // Minimum source confidence
          period: 0.75,   // Minimum period confidence
          schema: 0.85    // Minimum schema confidence
        }
      })
    }
  );

  console.log('Auto-processing enabled');
  ```

  ```python Python theme={null}
  response = requests.post(
      'https://api.royalti.io/file/enable-auto-processing',
      headers={
          'Authorization': f'Bearer {YOUR_API_TOKEN}',
          'Content-Type': 'application/json'
      },
      json={
          'enabled': True,
          'confidenceThresholds': {
              'source': 0.85,   # Minimum source confidence
              'period': 0.75,   # Minimum period confidence
              'schema': 0.85    # Minimum schema confidence
          }
      }
  )

  print('Auto-processing enabled')
  ```
</CodeGroup>

<Warning>
  Set thresholds carefully. Higher thresholds (0.9+) are safer but require more manual confirmations. Start with 0.85 and adjust based on accuracy.
</Warning>

### Check Auto-Processing Config

```javascript Node.js theme={null}
const response = await fetch(
  'https://api.royalti.io/file/auto-processing-config',
  { headers: { 'Authorization': `Bearer ${YOUR_API_TOKEN}` } }
);

const config = await response.json();

console.log('Enabled:', config.data.enabled);
console.log('Thresholds:', config.data.confidenceThresholds);
console.log('Success rate:', config.data.statistics.successRate, '%');
```

***

## Complete Example

Here's a full workflow from upload to processing:

<details>
  <summary>Show Complete Example</summary>

  ```javascript Node.js theme={null}
  const fs = require('fs');
  const path = require('path');

  async function processRoyaltyFile(filePath) {
    const fileName = path.basename(filePath);
    const fileSize = fs.statSync(filePath).size;

    // Step 1: Request upload URL with detection
    console.log('1️⃣ Requesting upload URL...');
    const uploadResponse = await fetch(
      'https://api.royalti.io/file/upload-url',
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${YOUR_API_TOKEN}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          fileName,
          fileSize,
          uploadType: 'royalty',
          enableDetection: true
        })
      }
    );

    const { signedUrl, sessionId } = await uploadResponse.json();

    // Step 2: Upload file
    console.log('2️⃣ Uploading file...');
    const fileBuffer = fs.readFileSync(filePath);
    await fetch(signedUrl, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/octet-stream' },
      body: fileBuffer
    });

    // Step 3: Wait for detection
    console.log('3️⃣ Waiting for detection...');
    let detection;
    for (let i = 0; i < 30; i++) {
      const detectionResponse = await fetch(
        `https://api.royalti.io/royalty/detection/${sessionId}`,
        { headers: { 'Authorization': `Bearer ${YOUR_API_TOKEN}` } }
      );

      detection = await detectionResponse.json();

      if (detection.data.status === 'detected' ||
          detection.data.status === 'auto_confirmed') {
        break;
      }

      await new Promise(resolve => setTimeout(resolve, 2000));
    }

    console.log(`   Detected: ${detection.data.detectedValues.source}`);
    console.log(`   Confidence: ${detection.data.confidence.source}`);

    // Step 4: Check if auto-processed
    if (detection.data.status === 'auto_confirmed') {
      console.log('✓ File auto-processed!');
      return;
    }

    // Step 5: Manual confirmation
    console.log('4️⃣ Confirming detection...');
    const sourcesResponse = await fetch(
      'https://api.royalti.io/royalty/sources',
      { headers: { 'Authorization': `Bearer ${YOUR_API_TOKEN}` } }
    );
    const sources = await sourcesResponse.json();
    const source = sources.data.sources.find(
      s => s.name === detection.data.detectedValues.source
    );

    const confirmResponse = await fetch(
      `https://api.royalti.io/royalty/detection/${sessionId}`,
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${YOUR_API_TOKEN}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          royaltySourceId: source.id,
          accountingPeriod: detection.data.detectedValues.period,
          processingOptions: { autoProcess: true }
        })
      }
    );

    const { processingJobId } = await confirmResponse.json();

    // Step 6: Monitor processing
    console.log('5️⃣ Monitoring processing...');
    while (true) {
      const statusResponse = await fetch(
        `https://api.royalti.io/royalty/processing/${processingJobId}`,
        { headers: { 'Authorization': `Bearer ${YOUR_API_TOKEN}` } }
      );

      const status = await statusResponse.json();
      const job = status.data;

      console.log(`   Progress: ${job.progress}%`);

      if (job.status === 'completed') {
        console.log('✓ Processing complete!');
        break;
      }

      if (job.status === 'failed') {
        console.error('✗ Processing failed:', job.errors);
        throw new Error('Processing failed');
      }

      await new Promise(resolve => setTimeout(resolve, 3000));
    }
  }

  // Usage
  processRoyaltyFile('./spotify_2024-01.csv')
    .then(() => console.log('Done!'))
    .catch(err => console.error('Error:', err));
  ```
</details>

***

## Best Practices

### File Naming

Use consistent naming for better detection accuracy:

<Tabs>
  <Tab title="Good">
    ```
    spotify_2024-01.csv
    apple_music_2024-Q1.xlsx
    youtube_january_2024.csv
    ```

    Pattern: `{source}_{period}.{ext}`
  </Tab>

  <Tab title="Bad">
    ```
    report_jan.csv
    data.xlsx
    royalties_123.csv
    ```

    These won't auto-detect reliably
  </Tab>
</Tabs>

### Error Handling

Always handle errors gracefully:

```javascript Node.js theme={null}
try {
  const detection = await waitForDetection(sessionId);

  if (detection.confidence.source < 0.6) {
    console.warn('Low confidence - manual review recommended');
  }

  // Continue with confirmation...
} catch (error) {
  if (error.message.includes('expired')) {
    console.error('Session expired - please re-upload');
  } else if (error.message.includes('timeout')) {
    console.error('Detection took too long - check file format');
  } else {
    console.error('Unexpected error:', error);
  }
}
```

### Timeout Handling

Don't poll indefinitely:

```javascript Node.js theme={null}
const MAX_DETECTION_TIME = 60000; // 60 seconds
const POLL_INTERVAL = 2000; // 2 seconds
const MAX_ATTEMPTS = MAX_DETECTION_TIME / POLL_INTERVAL;

for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
  // Poll detection...
}
```

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="Low confidence scores">
    **Problem:** Detection confidence is consistently below 0.8

    **Solutions:**

    * Use standard filename format: `{source}_{period}.{ext}`
    * Ensure column headers match expected schema
    * Check file isn't corrupted or empty
    * Contact support to create custom patterns for your files
  </Accordion>

  <Accordion title="Detection session expired">
    **Problem:** Error "Detection session expired after 24 hours"

    **Solutions:**

    * Process files within 24 hours of upload
    * Enable auto-processing for faster handling
    * Re-upload the file to create a new session
  </Accordion>

  <Accordion title="Wrong source detected">
    **Problem:** System detects incorrect source

    **Solutions:**

    * Check filename doesn't contain misleading keywords
    * Manually confirm with correct source ID
    * Contact support to improve detection patterns for your file format
  </Accordion>

  <Accordion title="Processing stuck in 'waiting' status">
    **Problem:** Job never progresses from waiting to active

    **Solutions:**

    * Check queue health (if you have admin access)
    * Wait up to 5 minutes during high load
    * Contact support if stuck longer than 10 minutes
  </Accordion>
</AccordionGroup>

***

## API Reference

Related endpoints:

* [POST /file/upload-url](/api-reference/file/post-file-upload-url) - Request upload URL
* [GET /file/detection/{sessionId}](/api-reference/file/get-file-detection-id) - Check detection status
* [POST /file/detection/{sessionId}](/api-reference/file/post-file-detection-id) - Confirm detection
* [GET /file/sources](/api-reference/file/get-file-sources) - List royalty sources (use `includeAll=false` for tenant-specific sources only)
* [GET /file/processing/{jobId}](/api-reference/file/get-file-processing-id) - Check processing status
* [POST /file/enable-auto-processing](/api-reference/file/post-file-enable-auto-processing) - Configure auto-processing
* [GET /file/auto-processing-config](/api-reference/file/get-file-auto-processing-config) - Get config

***

## Next Steps

* [Royalty Management](/guides/royalty-management) - View analytics and reports
* [Financial Data](/guides/financial-data-management) - Track earnings

***

## Support

Need help? Contact us:

* Email: [api@royalti.io](mailto:api@royalti.io)
* Documentation: [https://apidocs.royalti.io](https://apidocs.royalti.io)
* Status: [https://status.royalti.io](https://status.royalti.io)
