Account Management API¶
Status: Implemented
Last Updated: January 10, 2026
Location: app/api/account_routes.py
Overview¶
The Account Management API provides endpoints for users to manage their account lifecycle, including account deletion with a 30-day grace period and GDPR-compliant data export functionality.
Endpoints¶
Account Status¶
Returns the current account status including deletion state.
Response:
{
"user_id": "uuid",
"phone": "+15551234567",
"name": "John Doe",
"email": "john@example.com",
"created_at": "2026-01-01T00:00:00Z",
"subscription_status": "active",
"deletion_requested": false,
"deletion_scheduled_for": null
}
Request Account Deletion¶
Requests account deletion with a 30-day grace period. The user can cancel during this period.
Request Body:
Response (200 OK):
{
"message": "Account scheduled for deletion",
"deletion_date": "2026-02-10",
"can_cancel_until": "2026-02-10"
}
Error Responses:
- 400 INVALID_CONFIRMATION - Confirmation phrase doesn't match
- 400 ALREADY_SCHEDULED - Account already scheduled for deletion
Side Effects:
- Sets deletion_requested_at and deletion_scheduled_for on User
- Cancels active Stripe subscription (cancel at period end)
- Background job will permanently delete after 30 days
Rate Limit: 3 requests per hour
Cancel Account Deletion¶
Cancels a pending account deletion within the 30-day grace period.
Response (200 OK):
Error Responses:
- 400 NO_DELETION_PENDING - No deletion request exists
- 400 GRACE_PERIOD_EXPIRED - Cannot cancel after 30 days
Side Effects:
- Clears deletion_requested_at and deletion_scheduled_for
- Reactivates subscription if it was set to cancel
Request Data Export¶
Requests a full data export for GDPR compliance. Export is processed in the background.
Response (200 OK):
{
"job_id": "uuid",
"status": "queued",
"message": "Export queued. Check status with GET /account/export/{job_id}"
}
Rate Limit: 2 requests per day
Get Export Status¶
Checks the status of a data export job.
Response (Processing):
{
"job_id": "uuid",
"status": "processing",
"download_url": null,
"expires_at": null,
"error": null
}
Response (Ready):
{
"job_id": "uuid",
"status": "ready",
"download_url": "/account/export/{job_id}/download",
"expires_at": "2026-01-17T00:00:00Z",
"error": null
}
Status Values:
- queued - Job is waiting to be processed
- processing - Job is currently running
- ready - Export is complete and available for download
- failed - Export failed (see error field)
- expired - Download link has expired
Download Export¶
Downloads the exported data as a JSON file.
Response:
- Content-Type: application/json
- Content-Disposition: attachment; filename="archety_export_{job_id}.json"
Export Contents:
{
"export_version": "1.0",
"exported_at": "2026-01-10T00:00:00Z",
"user_id": "uuid",
"profile": {
"phone": "+15551234567",
"name": "John Doe",
"pronouns": "he/him",
"email": "john@example.com",
"display_name": "Johnny",
"timezone": "America/Los_Angeles",
"created_at": "2026-01-01T00:00:00Z",
"last_active_at": "2026-01-10T00:00:00Z"
},
"subscription": {
"status": "active",
"plan": "monthly",
"credit_balance": 500,
"trial_started_at": null,
"trial_expires_at": null
},
"personality_profile": {
"openness": 75,
"conscientiousness": 60,
"extraversion": 45,
"agreeableness": 80,
"neuroticism": 35,
"personality_summary": {...},
"top_personality_traits": ["empathetic", "creative"],
"top_interests": ["music", "travel"],
"core_values": ["creativity", "connection"],
"lifestyle_traits": ["outdoorsy"],
"selected_persona_id": "sage",
"total_photos_analyzed": 10,
"last_analysis_at": "2026-01-01T00:00:00Z"
},
"traits": [
{
"name": "adventurous",
"category": "personality",
"confidence": 85,
"strength": 70,
"created_at": "2026-01-01T00:00:00Z"
}
],
"relationships": [
{
"persona_id": "sage",
"trust_score": 75,
"rapport_score": 80,
"stage": "friend",
"updated_at": "2026-01-10T00:00:00Z"
}
],
"connected_services": [
{
"scope": "calendar",
"connected_at": "2026-01-05T00:00:00Z",
"expires_at": "2026-01-12T00:00:00Z"
}
],
"miniapp_sessions": [
{
"app_id": "trip_planner",
"state": "planning",
"started_at": "2026-01-08T00:00:00Z",
"ended_at": null,
"message_count": 15
}
],
"memories": [
{
"id": "mem_123",
"text": "User loves hiking in the mountains",
"category": "interests",
"created_at": "2026-01-02T00:00:00Z"
}
],
"billing": {
"customer_id": "cus_xxx",
"note": "For detailed billing history, use GET /billing/history"
}
}
Error Responses:
- 404 - Job not found or expired
- 403 - Not authorized (wrong user)
- 400 - Export not ready yet
- 410 - Export has expired (after 7 days)
Billing History¶
Billing history is available through the payment routes:
Response:
{
"transactions": [
{
"id": "pi_xxx",
"date": "2026-01-01T00:00:00Z",
"amount": 500,
"currency": "usd",
"description": "Trial",
"status": "succeeded",
"invoice_url": "https://...",
"receipt_url": "https://..."
}
],
"has_more": false,
"total_count": 1
}
Database Schema¶
The following columns support account deletion:
-- In users table
deletion_requested_at TIMESTAMPTZ -- When deletion was requested
deletion_scheduled_for TIMESTAMPTZ -- When permanent deletion will occur
-- Index for finding accounts to delete
CREATE INDEX idx_users_deletion_scheduled
ON users(deletion_scheduled_for)
WHERE deletion_scheduled_for IS NOT NULL;
Background Jobs¶
Account Hard Delete Job¶
A background job (not yet implemented) should:
1. Run daily
2. Find users where deletion_scheduled_for < NOW()
3. Permanently delete:
- All memories from Supermemory
- All database records (cascade)
- Stripe customer data
4. Send confirmation email
Security Considerations¶
- Authentication Required - All endpoints require valid JWT token
- Rate Limiting - Deletion (3/hour), Export (2/day)
- Confirmation Phrase - Deletion requires exact phrase match
- Grace Period - 30 days to recover from accidental deletion
- Export Expiry - Download links expire after 7 days
- Ownership Verification - Users can only access their own exports
Related User Stories¶
- US-10.3: Export My Data - GDPR-compliant data export
- US-10.4: Delete My Account - Account deletion with grace period
- US-9.2: View Billing History - Transaction history
Implementation Notes¶
Current Limitations¶
-
In-Memory Export Storage - Export jobs are stored in memory. In production, should use Redis or database table for persistence across restarts.
-
Memories Export - Currently attempts to fetch from Supermemory but may fail if service unavailable. Gracefully degrades.
-
Hard Delete Job - Background job for permanent deletion after 30 days is not yet implemented.
Future Enhancements¶
- Upload exports to Supabase Storage with signed URLs
- Send email notification when export is ready
- Implement hard delete background job
- Add PDF export format option
- Add confirmation email for deletion request