API: [API Name]

Status

Draft / Ready for Integration / Testing / Deprecated

Owner

[Backend Developer Name]

Last Updated

[YYYY-MM-DD]

1. Purpose

[One sentence — what this API does and why it exists.]

2. API Type

[Choose one]

  • Direct Supabase Table Insert / Select / Update / Delete
  • Supabase RPC Function
  • Supabase Edge Function
  • Xano Endpoint
  • Django REST Endpoint

3. Endpoint / Table / Function

Table: public.[table_name]
Function: [function_name]
Endpoint: POST /functions/v1/[function-name]

4. Auth Required

Yes / No

If yes — frontend must pass the logged-in user's access token:

Authorization: Bearer <access_token>

6. Request Payload

{
  "field_name": "string",
  "field_name": "uuid",
  "field_name": "string (optional)"
}

5. Permission Required

Who Can Call Allowed?
OwnerYes
AdminYes
Manager[Yes / No]
Member[Yes / No]
ViewerNo
AnonymousNo

7. Required Fields

Field Type Required Validation Rules
field_name string Yes [e.g. 2–100 characters]
field_name uuid Yes Must be accessible by the calling user
field_name string No Max [N] characters

8. Success Response

{
  "success": true,
  "data": {
    "id": "uuid",
    "field_name": "value"
  },
  "message": "[Human-readable success message]"
}

10. RLS / Permission Policies Involved

  • [List the RLS policies or permission checks that affect this API]
  • [e.g. workspace_members: only members of the workspace can insert]

9. Error Responses

401 — UNAUTHENTICATED

When: User is not logged in. Token is missing or expired.

{
  "success": false,
  "error": {
    "code": "UNAUTHENTICATED",
    "message": "Your session has expired. Please log in again."
  }
}

Frontend action: Redirect to login. Clear local auth state if needed.

403 — FORBIDDEN

When: User is authenticated but does not have the required role or workspace access.

{
  "success": false,
  "error": {
    "code": "FORBIDDEN",
    "message": "You do not have permission to perform this action."
  }
}

Frontend action: Show permission error. Hide or disable the action for unauthorized roles.

422 — VALIDATION_ERROR

When: A required field is missing or a field does not meet validation rules.

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Please fix the highlighted fields.",
    "fields": {
      "field_name": "Error message for this specific field."
    }
  }
}

Frontend action: Show field-level error messages next to each invalid field.

409 — CONFLICT

When: [Describe when a conflict occurs — e.g., a record with this name already exists in the workspace.]

{
  "success": false,
  "error": {
    "code": "CONFLICT",
    "message": "[Human-readable conflict message]"
  }
}

Frontend action: Show the conflict message near the relevant field.

404 — NOT_FOUND

When: The requested record does not exist or the user does not have access to it.

{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "The requested resource was not found."
  }
}

Frontend action: Show a not-found state or redirect.

500 — INTERNAL_ERROR

When: Unexpected server or database error.

{
  "success": false,
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Something went wrong. Please try again."
  }
}

Frontend action: Show retry option. Do not clear form data.

11. Frontend Implementation Notes

  • [What frontend must do before calling — e.g., must be logged in, must have workspace_id]
  • [Fields frontend should NOT send — e.g., created_by is set server-side from auth token]
  • [Loading state expected — disable button, show "Saving..."]
  • [On success — redirect to X / refresh list / show notification]
  • [On 401 — redirect to login]
  • [On 403 — show permission message, hide the action button]

12. Frontend Code Example

Supabase RPC:

const { data, error } = await supabase.rpc("function_name", {
  p_field_name: value,
  p_field_name: value,
});

if (error) {
  // handle error
}

Supabase Direct Table:

const { data, error } = await supabase
  .from("table_name")
  .insert({ field: value })
  .select("id, field1, field2")
  .single();

Xano / Django (fetch):

const response = await fetch("/api/endpoint", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${accessToken}`,
  },
  body: JSON.stringify({ field_name: value }),
});

const result = await response.json();

13. Test Cases

# Test Case Expected Result
1[Allowed role] performs action successfully200, returns data
2Viewer role attempts action403 FORBIDDEN
3Non-member attempts action403 FORBIDDEN
4Anonymous user attempts action401 UNAUTHENTICATED
5Required field [field] is missing422 VALIDATION_ERROR
6Field [field] fails validation rules422 VALIDATION_ERROR
7Duplicate [field] in same workspace409 CONFLICT
8User attempts action on another workspace's data403 FORBIDDEN or 404 NOT_FOUND

Standard Error Codes Reference

Code HTTP When to Use
UNAUTHENTICATED401User is not logged in or token is expired
FORBIDDEN403Authenticated but lacks permission
VALIDATION_ERROR422Input is missing or fails validation
NOT_FOUND404Record does not exist or user cannot access it
CONFLICT409Duplicate or conflicting data
RATE_LIMITED429Too many requests
INTERNAL_ERROR500Unexpected server error
EXTERNAL_SERVICE_ERROR502Third-party service call failed
DATABASE_ERROR500Database operation failed