Skip to Content

Delete Project

Permanently delete a project and all its files. This operation is irreversible and removes the project’s GCS bucket and all stored files.

Warning: Deleting a project is permanent and irreversible. All files in the project will be deleted along with the GCS bucket.

Endpoint

DELETE /api/projects/{project_id}

Authentication

Required: API key

Authorization: Bearer YOUR_API_KEY

Request

Path Parameters

ParameterTypeRequiredDescription
project_idUUIDYesUnique project identifier

Headers

HeaderValueRequired
AuthorizationBearer YOUR_API_KEYYes

Response

Success Response (200 OK)

{ "message": "Project deleted successfully", "project_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }

Response Fields

FieldTypeDescription
messagestringSuccess message
project_idUUIDID of deleted project

Error Responses

StatusErrorDescriptionSolution
401UnauthorizedMissing or invalid authenticationVerify Bearer token or API key
403ForbiddenCannot delete default project OR not ownerCheck if default project or verify ownership
404Not FoundProject doesn’t existCheck project_id is correct
422Unprocessable EntityInvalid UUID formatEnsure project_id is valid UUID
429Too Many RequestsRate limit exceededWait and retry
500Internal Server ErrorServer errorContact support if persists

Example Error Response (403 - Default Project):

{ "detail": "Cannot delete default project" }

Example Error Response (404 Not Found):

{ "detail": "Project not found" }

Deletion Process

When you delete a project, the following happens:

  1. Verification: System checks you own the project and it’s not the default
  2. File Deletion: All files in the project are deleted from GCS
  3. Database Cleanup: Project record marked as deleted/inactive
  4. Bucket Removal: GCS bucket is deleted
  5. Confirmation: Success response returned

Duration: Typically 500ms to 5 seconds depending on file count.

Examples

cURL

curl -X DELETE "https://markdownapi.io/api/projects/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \ -H "Authorization: Bearer YOUR_TOKEN"

Python (httpx)

import httpx import os from uuid import UUID async def delete_project(project_id: UUID): """Delete project with confirmation and error handling.""" api_key = os.getenv("MARKDOWN_API_KEY") async with httpx.AsyncClient() as client: try: response = await client.delete( f"https://markdownapi.io/api/projects/{project_id}", headers={"Authorization": f"Bearer {api_key}"}, timeout=30.0 # Longer timeout for deletion ) response.raise_for_status() result = response.json() print(f"✓ Project deleted: {result['project_id']}") print(f" {result['message']}") return result except httpx.HTTPStatusError as e: if e.response.status_code == 403: error = e.response.json() if "default" in error.get("detail", "").lower(): print(f"✗ Cannot delete default project") else: print(f"✗ Access denied") elif e.response.status_code == 404: print(f"✗ Project not found or already deleted") else: print(f"✗ HTTP error: {e.response.status_code}") raise # Usage # await delete_project(UUID("a1b2c3d4-e5f6-7890-abcd-ef1234567890"))

TypeScript

interface DeleteProjectResponse { message: string; project_id: string; } async function deleteProject(projectId: string): Promise<DeleteProjectResponse> { const apiKey = process.env.MARKDOWN_API_KEY; try { const response = await fetch( `https://markdownapi.io/api/projects/${projectId}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${apiKey!}`, }, } ); if (!response.ok) { if (response.status === 403) { const error = await response.json(); if (error.detail?.toLowerCase().includes('default')) { throw new Error('Cannot delete default project'); } throw new Error('Access denied'); } else if (response.status === 404) { throw new Error('Project not found or already deleted'); } throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const result: DeleteProjectResponse = await response.json(); console.log(`✓ Project deleted: ${result.project_id}`); console.log(` ${result.message}`); return result; } catch (error) { console.error('✗ Failed to delete project:', error); throw error; } } // Usage // await deleteProject('a1b2c3d4-e5f6-7890-abcd-ef1234567890');

Common Use Cases

1. Delete with Confirmation

async def delete_project_with_confirmation(project_id: UUID): """Delete project with user confirmation.""" project = await get_project(project_id) print(f"About to delete project: {project['name']}") print(f" Files: {project['file_count']}") print(f" Size: {project['total_size_bytes']} bytes") confirm = input("Type project name to confirm deletion: ") if confirm != project['name']: print("✗ Deletion cancelled - name mismatch") return None return await delete_project(project_id)

2. Backup Before Delete

async def backup_and_delete_project(project_id: UUID, backup_path: str): """Backup all files before deleting project.""" import os # Get all files files = await list_files(project_id) # Download each file for file in files: content = await download_file(project_id, file['filename']) file_path = os.path.join(backup_path, file['filename']) with open(file_path, 'wb') as f: f.write(content) print(f"✓ Backed up {len(files)} files to {backup_path}") # Delete project return await delete_project(project_id)

3. Bulk Delete

import asyncio async def delete_multiple_projects(project_ids: list[UUID]): """Delete multiple projects (use with caution).""" async def delete_single(project_id): try: return await delete_project(project_id) except Exception as e: print(f"Failed to delete {project_id}: {e}") return None results = await asyncio.gather(*[ delete_single(pid) for pid in project_ids ]) successful = [r for r in results if r is not None] print(f"Deleted {len(successful)}/{len(project_ids)} projects") return successful

4. Safe Delete (Check First)

async def safe_delete_project(project_id: UUID): """Delete project with safety checks.""" # Check project exists and get details try: project = await get_project(project_id) except httpx.HTTPStatusError as e: if e.response.status_code == 404: print("✓ Project already deleted") return None raise # Check if default if project['is_default']: raise ValueError("Cannot delete default project") # Check file count if project['file_count'] > 100: print(f"⚠ Warning: Project has {project['file_count']} files") confirm = input("Continue? (yes/no): ") if confirm.lower() != 'yes': print("✗ Deletion cancelled") return None # Proceed with deletion return await delete_project(project_id)

Protection Mechanisms

Default Project Protection

The default project cannot be deleted:

# This will fail with 403 Forbidden try: await delete_project(default_project_id) except httpx.HTTPStatusError as e: print(e.response.json()) # {"detail": "Cannot delete default project"}

Ownership Verification

You can only delete your own projects:

# Attempting to delete another user's project returns 403 try: await delete_project(other_users_project_id) except httpx.HTTPStatusError as e: print(e.response.json()) # {"detail": "You do not have permission to access this resource"}

Performance Characteristics

Deletion Duration

Files in ProjectTypical DurationNotes
0-10 files500ms - 1sFast deletion
10-100 files1s - 3sModerate deletion
100-1000 files3s - 10sSlower deletion
1000+ files10s - 30sMay require patience

Timeout Recommendations:

  • Set client timeout to at least 30 seconds
  • Implement progress indication for large projects
  • Consider async notification for very large projects

Best Practices

1. Always Backup First

# Good practice await backup_and_delete_project(project_id, "/backups") # Risky await delete_project(project_id) # No backup!

2. Verify Project ID

# Good - verify before delete project = await get_project(project_id) print(f"Deleting: {project['name']}") await delete_project(project_id) # Risky - delete without verification await delete_project(project_id)

3. Use Soft Delete Pattern

Consider marking projects as inactive instead of deleting:

# Instead of deleting, mark as inactive await update_project(project_id, name=f"[DELETED] {project['name']}") # Then later, actually delete when confirmed

4. Handle Idempotency

async def delete_project_idempotent(project_id: UUID): """Delete project with idempotent behavior.""" try: return await delete_project(project_id) except httpx.HTTPStatusError as e: if e.response.status_code == 404: # Already deleted - treat as success return {"message": "Project already deleted", "project_id": str(project_id)} raise

Recovery

No Recovery: Deleted projects cannot be recovered. All files and the GCS bucket are permanently removed.

To prevent accidental deletion:

  1. Implement confirmation flows
  2. Backup files before deletion
  3. Use soft delete (mark inactive) before hard delete
  4. Restrict deletion permissions
  5. Audit deletion operations

See Also

Last updated on