The Delete Source endpoint allows you to permanently remove documents from your GraphorLM project using the REST API. This endpoint provides a secure way to clean up your document collection, automatically updating related flows and removing all associated data including partition nodes and metadata.

Endpoint Overview

HTTP Method

DELETE

Authentication

This endpoint requires authentication using an API token. You must include your API token as a Bearer token in the Authorization header.

Learn how to create and manage API tokens in the API Tokens guide.

Request Format

Headers

HeaderValueRequired
AuthorizationBearer YOUR_API_TOKEN✅ Yes
Content-Typeapplication/json✅ Yes

Request Body

The request must be sent as JSON with the following field:

FieldTypeDescriptionRequired
file_namestringName of the file to delete (case-sensitive)✅ Yes

Important Considerations

Request Example

{
  "file_name": "document.pdf"
}

Response Format

Success Response (200 OK)

{
  "status": "success",
  "message": "Source deleted successfully",
  "file_name": "document.pdf",
  "project_id": "550e8400-e29b-41d4-a716-446655440000",
  "project_name": "My Project"
}

Response Fields

FieldTypeDescription
statusstringDeletion result (typically “success”)
messagestringHuman-readable confirmation message
file_namestringName of the deleted file
project_idstringUUID of the project the file was removed from
project_namestringName of the project

Code Examples

JavaScript/Node.js

const deleteDocument = async (apiToken, fileName) => {
  const response = await fetch('https://sources.graphorlm.com/delete', {
    method: 'DELETE',
    headers: {
      'Authorization': `Bearer ${apiToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      file_name: fileName
    })
  });

  if (response.ok) {
    const result = await response.json();
    console.log('Deletion successful:', result);
    return result;
  } else {
    const error = await response.text();
    throw new Error(`Deletion failed: ${response.status} ${error}`);
  }
};

// Usage
deleteDocument('grlm_your_api_token_here', 'document.pdf')
  .then(result => console.log('Document deleted:', result.file_name))
  .catch(error => console.error('Error:', error));

Python

import requests
import json

def delete_document(api_token, file_name):
    url = "https://sources.graphorlm.com/delete"
    
    headers = {
        "Authorization": f"Bearer {api_token}",
        "Content-Type": "application/json"
    }
    
    payload = {
        "file_name": file_name
    }
    
    response = requests.delete(
        url, 
        headers=headers, 
        json=payload, 
        timeout=60
    )
    
    if response.status_code == 200:
        result = response.json()
        print(f"Deletion successful: {result['file_name']}")
        return result
    else:
        response.raise_for_status()

# Usage - with confirmation prompt for safety
def safe_delete_document(api_token, file_name):
    """Delete document with confirmation prompt for safety."""
    print(f"⚠️  WARNING: This will permanently delete '{file_name}'")
    confirmation = input("Type 'DELETE' to confirm: ")
    
    if confirmation == 'DELETE':
        try:
            result = delete_document(api_token, file_name)
            print(f"✅ Document '{file_name}' deleted successfully")
            return result
        except requests.exceptions.RequestException as e:
            print(f"❌ Error deleting document: {e}")
            raise
    else:
        print("❌ Deletion cancelled")
        return None

# Usage
try:
    result = safe_delete_document("grlm_your_api_token_here", "document.pdf")
except requests.exceptions.RequestException as e:
    print(f"Error: {e}")

cURL

curl -X DELETE https://sources.graphorlm.com/delete \
  -H "Authorization: Bearer grlm_your_api_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "file_name": "document.pdf"
  }'

PHP

<?php
function deleteDocument($apiToken, $fileName) {
    $url = "https://sources.graphorlm.com/delete";
    
    $headers = [
        "Authorization: Bearer " . $apiToken,
        "Content-Type: application/json"
    ];
    
    $payload = json_encode([
        'file_name' => $fileName
    ]);
    
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
    curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 60);
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($httpCode === 200) {
        return json_decode($response, true);
    } else {
        throw new Exception("Deletion failed with HTTP code: " . $httpCode);
    }
}

// Safe deletion function with confirmation
function safeDeleteDocument($apiToken, $fileName) {
    echo "⚠️  WARNING: This will permanently delete '$fileName'\n";
    echo "Type 'DELETE' to confirm: ";
    $confirmation = trim(fgets(STDIN));
    
    if ($confirmation === 'DELETE') {
        try {
            $result = deleteDocument($apiToken, $fileName);
            echo "✅ Document '$fileName' deleted successfully\n";
            return $result;
        } catch (Exception $e) {
            echo "❌ Error: " . $e->getMessage() . "\n";
            throw $e;
        }
    } else {
        echo "❌ Deletion cancelled\n";
        return null;
    }
}

// Usage
try {
    $result = safeDeleteDocument("grlm_your_api_token_here", "document.pdf");
    if ($result) {
        echo "Project: " . $result['project_name'] . "\n";
        echo "Status: " . $result['status'] . "\n";
    }
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}
?>

Error Responses

Common Error Codes

Status CodeError TypeDescription
400Bad RequestInvalid request format or missing file name
401UnauthorizedInvalid or missing API token
403ForbiddenAccess denied to the specified project
404Not FoundFile not found in the project
500Internal Server ErrorServer-side error during deletion

Error Response Format

{
  "detail": "Source not found"
}

Error Examples

Best Practices

Pre-Deletion Verification

Safety Measures

  • Implement confirmation prompts in interactive applications
  • Log all deletion operations for audit trails
  • Use descriptive variable names to avoid accidental deletions
  • Test with non-production data when implementing deletion features
  • Consider soft deletion patterns for critical applications

Error Handling

  • Implement retry logic for transient network errors (not for 404/403 errors)
  • Validate file names before making deletion requests
  • Handle batch operations carefully to avoid partial failures
  • Provide clear error messages to end users

Integration Examples

Batch Deletion with Safety Checks

import requests
import time
from typing import List, Dict

def safe_batch_delete(api_token: str, file_names: List[str], 
                     confirm_each: bool = True) -> Dict:
    """Safely delete multiple files with comprehensive checks."""
    
    # First, get current sources to validate all files exist
    print("🔍 Verifying files exist before deletion...")
    try:
        sources_response = requests.get(
            "https://sources.graphorlm.com",
            headers={"Authorization": f"Bearer {api_token}"},
            timeout=30
        )
        sources = sources_response.json() if sources_response.status_code == 200 else []
        available_files = {s['file_name']: s for s in sources}
    except Exception as e:
        print(f"❌ Error fetching source list: {e}")
        return {"error": "Could not verify file existence"}
    
    # Validate all files exist
    missing_files = [f for f in file_names if f not in available_files]
    if missing_files:
        print(f"❌ Files not found: {missing_files}")
        return {"error": f"Missing files: {missing_files}"}
    
    print(f"✅ All {len(file_names)} files found in project")
    
    # Show summary
    total_size = sum(available_files[f]['file_size'] for f in file_names)
    print(f"\n📋 Deletion Summary:")
    print(f"  Files to delete: {len(file_names)}")
    print(f"  Total size: {total_size:,} bytes")
    
    for file_name in file_names:
        file_info = available_files[file_name]
        print(f"  - {file_name} ({file_info['file_type']}, {file_info['status']})")
    
    # Global confirmation
    if confirm_each:
        confirm = input(f"\n⚠️  Delete {len(file_names)} files permanently? (type 'DELETE'): ")
        if confirm != 'DELETE':
            print("❌ Batch deletion cancelled")
            return {"cancelled": True}
    
    # Perform deletions
    results = {"successful": [], "failed": []}
    
    for i, file_name in enumerate(file_names, 1):
        print(f"\n[{i}/{len(file_names)}] Deleting {file_name}...")
        
        try:
            response = requests.delete(
                "https://sources.graphorlm.com/delete",
                headers={
                    "Authorization": f"Bearer {api_token}",
                    "Content-Type": "application/json"
                },
                json={"file_name": file_name},
                timeout=60
            )
            
            if response.status_code == 200:
                result = response.json()
                results["successful"].append({
                    "file_name": file_name,
                    "result": result
                })
                print(f"  ✅ Deleted successfully")
            else:
                error_detail = response.text
                results["failed"].append({
                    "file_name": file_name,
                    "error": f"HTTP {response.status_code}: {error_detail}"
                })
                print(f"  ❌ Failed: {error_detail}")
                
        except Exception as e:
            results["failed"].append({
                "file_name": file_name,
                "error": str(e)
            })
            print(f"  ❌ Error: {e}")
        
        # Small delay between deletions
        if i < len(file_names):
            time.sleep(0.5)
    
    # Final summary
    print(f"\n🏁 Batch deletion complete:")
    print(f"  ✅ Successful: {len(results['successful'])}")
    print(f"  ❌ Failed: {len(results['failed'])}")
    
    return results

# Usage
files_to_delete = ["old_document.pdf", "temp_file.txt", "archive.docx"]
results = safe_batch_delete("grlm_your_token", files_to_delete)

Project Cleanup Tool

class ProjectCleanupTool {
  constructor(apiToken) {
    this.apiToken = apiToken;
    this.baseUrl = 'https://sources.graphorlm.com';
  }

  async getSourcesByStatus(status = null) {
    const response = await fetch(this.baseUrl, {
      headers: { 'Authorization': `Bearer ${this.apiToken}` }
    });
    
    if (!response.ok) {
      throw new Error(`Failed to fetch sources: ${response.status}`);
    }
    
    const sources = await response.json();
    return status ? sources.filter(s => s.status === status) : sources;
  }

  async deleteSource(fileName) {
    const response = await fetch(`${this.baseUrl}/delete`, {
      method: 'DELETE',
      headers: {
        'Authorization': `Bearer ${this.apiToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ file_name: fileName })
    });

    if (!response.ok) {
      const error = await response.text();
      throw new Error(`Deletion failed: ${response.status} ${error}`);
    }

    return response.json();
  }

  async cleanupFailedSources(interactive = true) {
    console.log('🔍 Finding failed sources...');
    
    const failedSources = await this.getSourcesByStatus('Failed');
    
    if (failedSources.length === 0) {
      console.log('✅ No failed sources found');
      return { deleted: 0, skipped: 0 };
    }

    console.log(`📋 Found ${failedSources.length} failed sources:`);
    failedSources.forEach((source, i) => {
      console.log(`  ${i + 1}. ${source.file_name} (${source.file_type})`);
    });

    if (interactive) {
      const readline = require('readline').createInterface({
        input: process.stdin,
        output: process.stdout
      });

      const answer = await new Promise(resolve => {
        readline.question(`\n⚠️  Delete all ${failedSources.length} failed sources? (yes/no): `, resolve);
      });
      
      readline.close();

      if (answer.toLowerCase() !== 'yes') {
        console.log('❌ Cleanup cancelled');
        return { deleted: 0, skipped: failedSources.length };
      }
    }

    // Delete failed sources
    let deleted = 0;
    for (const source of failedSources) {
      try {
        await this.deleteSource(source.file_name);
        console.log(`✅ Deleted: ${source.file_name}`);
        deleted++;
        
        // Small delay between deletions
        await new Promise(resolve => setTimeout(resolve, 500));
      } catch (error) {
        console.log(`❌ Failed to delete ${source.file_name}: ${error.message}`);
      }
    }

    console.log(`\n🏁 Cleanup complete: ${deleted} sources deleted`);
    return { deleted, skipped: failedSources.length - deleted };
  }

  async cleanupOldSources(daysOld = 30, fileTypes = []) {
    console.log(`🔍 Finding sources older than ${daysOld} days...`);
    
    const allSources = await this.getSourcesByStatus();
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - daysOld);

    const oldSources = allSources.filter(source => {
      const createdAt = new Date(source.created_at || 0);
      const matchesAge = createdAt < cutoffDate;
      const matchesType = fileTypes.length === 0 || fileTypes.includes(source.file_type);
      return matchesAge && matchesType;
    });

    if (oldSources.length === 0) {
      console.log(`✅ No sources older than ${daysOld} days found`);
      return { deleted: 0 };
    }

    console.log(`📋 Found ${oldSources.length} old sources`);
    // Implementation continues...
  }
}

// Usage
const cleanup = new ProjectCleanupTool('grlm_your_token');

// Clean up failed sources
cleanup.cleanupFailedSources()
  .then(result => console.log('Cleanup result:', result))
  .catch(error => console.error('Cleanup error:', error));

File Lifecycle Management

from datetime import datetime, timedelta
from typing import List, Dict, Optional
import requests

class DocumentLifecycleManager:
    def __init__(self, api_token: str):
        self.api_token = api_token
        self.base_url = "https://sources.graphorlm.com"
    
    def get_headers(self):
        return {
            "Authorization": f"Bearer {self.api_token}",
            "Content-Type": "application/json"
        }
    
    def list_sources(self) -> List[Dict]:
        response = requests.get(self.base_url, headers=self.get_headers())
        response.raise_for_status()
        return response.json()
    
    def delete_source(self, file_name: str) -> Dict:
        response = requests.delete(
            f"{self.base_url}/delete",
            headers=self.get_headers(),
            json={"file_name": file_name}
        )
        response.raise_for_status()
        return response.json()
    
    def archive_by_criteria(self, 
                          max_age_days: Optional[int] = None,
                          file_types: Optional[List[str]] = None,
                          min_file_size: Optional[int] = None,
                          max_file_size: Optional[int] = None,
                          status_filter: Optional[str] = None,
                          dry_run: bool = True) -> Dict:
        """
        Archive (delete) sources based on multiple criteria.
        """
        
        print("🔍 Analyzing sources for archival...")
        sources = self.list_sources()
        
        candidates = []
        
        for source in sources:
            # Age check
            if max_age_days:
                # Note: This would need actual creation date from source
                # For demo purposes, we'll skip this check
                pass
            
            # File type check
            if file_types and source['file_type'] not in file_types:
                continue
            
            # Size checks
            size = source['file_size']
            if min_file_size and size < min_file_size:
                continue
            if max_file_size and size > max_file_size:
                continue
            
            # Status check
            if status_filter and source['status'] != status_filter:
                continue
            
            candidates.append(source)
        
        print(f"📋 Found {len(candidates)} sources matching criteria:")
        
        total_size = 0
        for source in candidates:
            size = source['file_size']
            total_size += size
            size_mb = size / (1024 * 1024)
            print(f"  - {source['file_name']} ({source['file_type']}, {size_mb:.1f}MB, {source['status']})")
        
        total_size_mb = total_size / (1024 * 1024)
        print(f"\nTotal size to be freed: {total_size_mb:.1f}MB")
        
        if dry_run:
            print("\n🔬 DRY RUN - No files will be deleted")
            return {
                "dry_run": True,
                "candidates": len(candidates),
                "total_size_mb": total_size_mb
            }
        
        # Confirm deletion
        confirm = input(f"\n⚠️  Delete {len(candidates)} sources? (type 'DELETE'): ")
        if confirm != 'DELETE':
            print("❌ Archival cancelled")
            return {"cancelled": True}
        
        # Perform deletions
        deleted = []
        failed = []
        
        for source in candidates:
            try:
                result = self.delete_source(source['file_name'])
                deleted.append(source['file_name'])
                print(f"✅ Archived: {source['file_name']}")
            except Exception as e:
                failed.append({"file_name": source['file_name'], "error": str(e)})
                print(f"❌ Failed: {source['file_name']} - {e}")
        
        return {
            "deleted": deleted,
            "failed": failed,
            "total_deleted": len(deleted),
            "total_failed": len(failed),
            "size_freed_mb": sum(s['file_size'] for s in candidates if s['file_name'] in deleted) / (1024 * 1024)
        }

# Usage examples
manager = DocumentLifecycleManager("grlm_your_token")

# Archive large files over 50MB
result = manager.archive_by_criteria(
    min_file_size=50 * 1024 * 1024,  # 50MB
    dry_run=True  # Check what would be deleted first
)

# Archive failed documents
result = manager.archive_by_criteria(
    status_filter="Failed",
    dry_run=False
)

# Archive specific file types
result = manager.archive_by_criteria(
    file_types=["tmp", "temp", "test"],
    dry_run=True
)

Troubleshooting

Next Steps

After successfully deleting your documents: