Files
hackerspace.sh/docs/API_REFERENCE.md
T

17 KiB

Hackerspace.sh - API Reference

Last Updated: 2026-03-10
Complete reference for all server actions, database operations, and API patterns.


Table of Contents

  1. Server Actions Overview
  2. Authentication Actions
  3. Task Actions
  4. Project Actions
  5. Member Actions
  6. Payment Actions
  7. Knowledge Base Actions
  8. Contact Actions
  9. Settings Actions
  10. Data Fetching Patterns
  11. Real-time Subscriptions
  12. Error Handling

1. Server Actions Overview

All mutations use Next.js Server Actions with 'use server' directive. Actions:

  • Authenticate via supabase.auth.getUser()
  • Validate membership and roles
  • Return { data?, error?, success? } objects
  • Call revalidatePath() for cache invalidation

Import Pattern

// In client components
import { createTask, claimTask } from '@/lib/actions'
import { signIn, signOut } from '@/lib/auth-actions'

// Usage
const result = await createTask({ title: 'Clean laser', type: 'chore' })
if (result.error) {
  setError(result.error)
} else {
  // Success - result.data contains the new task
}

2. Authentication Actions

File: lib/auth-actions.ts

signIn(email, password)

Authenticates user with email/password.

async function signIn(email: string, password: string): Promise<{
  data?: AuthData
  error?: string
}>

Parameters:

  • email (string, required): User email
  • password (string, required): User password

Returns:

  • { data: AuthData } on success
  • { error: string } on failure

Example:

const result = await signIn('[email protected]', 'password123')
if (result.error) {
  toast.error(result.error)
} else {
  router.push('/dashboard')
}

signUp(formData)

Creates new account and optionally creates/joins a space.

async function signUp(formData: {
  email: string
  password: string
  fullName: string
  action: 'create' | 'join'
  spaceName?: string
  spaceSlug?: string
  city?: string
  inviteCode?: string
}): Promise<{ data?: AuthData; error?: string }>

Parameters:

  • email (string, required): User email
  • password (string, required): Minimum 8 characters
  • fullName (string, required): Display name
  • action ('create' | 'join', required): Space action
  • spaceName (string): Required if action='create'
  • spaceSlug (string): Required if action='create'
  • city (string, optional): Space location
  • inviteCode (string): Required if action='join'

Behavior:

  • Creates auth user with metadata
  • DB trigger handle_space_signup creates space/member
  • Returns session if email confirmation disabled
  • Otherwise redirects to confirmation page

signOut()

Signs out current user and redirects to login.

async function signOut(): Promise<never>

Behavior:

  • Calls supabase.auth.signOut()
  • Redirects to /login

getUser()

Gets current authenticated user.

async function getUser(): Promise<User | null>

getCurrentMembership()

Gets current user's space membership with space details.

async function getCurrentMembership(): Promise<SpaceMember & { spaces: Space } | null>

createSpace(formData)

Creates a new space for authenticated user.

async function createSpace(formData: {
  spaceName: string
  spaceSlug: string
  spaceCity?: string
  displayName: string
}): Promise<{ success?: boolean; error?: string }>

Role Required: Authenticated (no existing space membership)

Behavior:

  • Uses admin client (bypasses RLS)
  • Creates space with auto-generated invite code
  • Creates member as admin/plus/current

joinSpace(formData)

Joins existing space via invite code.

async function joinSpace(formData: {
  inviteCode: string
  displayName: string
}): Promise<{ success?: boolean; error?: string }>

Role Required: Authenticated

Behavior:

  • Validates invite code
  • Creates member with status based on require_approval

3. Task Actions

File: lib/actions.ts

createTask(formData)

Creates a new task or chore.

async function createTask(formData: {
  title: string
  description?: string
  type: string          // 'task' | 'chore'
  area?: string
  recurrence?: string   // 'none' | 'daily' | 'weekly' | 'biweekly' | 'monthly'
  due_date?: string     // ISO date string
}): Promise<{ data?: Task; error?: string }>

Role Required: Member

Behavior:

  • Sets requested_by to current user
  • Sets status to 'open'
  • Logs activity
  • Revalidates /tasks and /dashboard

claimTask(taskId)

Claims an open task.

async function claimTask(taskId: string): Promise<{ success?: boolean; error?: string }>

Role Required: Member

Behavior:

  • Sets claimed_by to current user
  • Sets status to 'claimed'
  • Logs activity

completeTask(taskId)

Marks task as completed.

async function completeTask(taskId: string): Promise<{ success?: boolean; error?: string }>

Role Required: Member (ideally claimant)

Behavior:

  • Sets status to 'completed'
  • Sets completed_at and last_done_at
  • Logs activity

deleteTask(taskId)

Deletes a task.

async function deleteTask(taskId: string): Promise<{ success?: boolean; error?: string }>

Role Required: Member


4. Project Actions

createProject(formData)

Creates a new project.

async function createProject(formData: {
  title: string
  description?: string
  area?: string
  tags?: string[]
  due_date?: string
}): Promise<{ data?: Project; error?: string }>

Role Required: Member

Behavior:

  • Sets status to 'backlog'
  • Logs activity

updateProjectStatus(projectId, status)

Updates project kanban status.

async function updateProjectStatus(
  projectId: string, 
  status: string  // 'backlog' | 'in_progress' | 'review' | 'done' | 'blocked'
): Promise<{ success?: boolean; error?: string }>

Role Required: Member


deleteProject(projectId)

Deletes a project.

async function deleteProject(projectId: string): Promise<{ success?: boolean; error?: string }>

Role Required: Member


5. Member Actions

addMember(formData)

Manually adds a member to the space.

async function addMember(formData: {
  display_name: string
  email: string
  phone?: string
  handle?: string
  tier: string          // 'plus' | 'basic' | 'associate'
  role: string          // 'admin' | 'board' | 'treasurer' | 'member' | 'associate'
  joined_at?: string
  has_card_access?: boolean
}): Promise<{ data?: SpaceMember; error?: string }>

Role Required: Admin or Board

Note: Creates member without user_id (offline member). User can claim via signup later.


updateMember(memberId, updates)

Updates member details.

async function updateMember(memberId: string, updates: {
  display_name?: string
  email?: string
  phone?: string
  handle?: string
  tier?: string
  role?: string
  status?: string
  has_card_access?: boolean
  payment_status?: string
  payment_note?: string
}): Promise<{ success?: boolean; error?: string }>

Role Required: Admin or Board


approveMember(memberId)

Approves a pending member.

async function approveMember(memberId: string): Promise<{ success?: boolean; error?: string }>

Role Required: Admin or Board

Behavior:

  • Sets status to 'current'
  • Sets approved to true
  • Logs activity

removeMember(memberId)

Removes member from space.

async function removeMember(memberId: string): Promise<{ success?: boolean; error?: string }>

Role Required: Admin only


importMembers(rows)

Bulk import members from CSV data.

async function importMembers(rows: Array<{
  display_name: string
  email: string
  phone?: string
  tier?: string
  joined_at?: string
  last_paid_at?: string
  has_card_access?: boolean
}>): Promise<{ data?: SpaceMember[]; count?: number; error?: string }>

Role Required: Admin or Board

Behavior:

  • Upserts on (space_id, email) to avoid duplicates

6. Payment Actions

logCashPayment(formData)

Records a cash payment.

async function logCashPayment(formData: {
  amount: number
  from_note: string
  member_id?: string
  transaction_date?: string
}): Promise<{ data?: Payment; error?: string }>

Role Required: Treasurer, Board, or Admin

Behavior:

  • Sets platform to 'cash'
  • If member_id provided, sets link_status to 'linked'
  • Updates member's last_paid_at if linked
  • Logs activity

linkPaymentToMember(paymentId, memberId)

Links an unlinked payment to a member.

async function linkPaymentToMember(
  paymentId: string, 
  memberId: string
): Promise<{ success?: boolean; error?: string }>

Role Required: Treasurer, Board, or Admin

Behavior:

  • Updates payment member_id and link_status
  • Updates member last_paid_at and payment_status

importPaymentsCsv(rows)

Bulk import payments.

async function importPaymentsCsv(rows: Array<{
  platform: string
  amount: number
  from_identifier: string
  from_note?: string
  transaction_date: string
}>): Promise<{ data?: Payment[]; count?: number; error?: string }>

Role Required: Treasurer, Board, or Admin


7. Knowledge Base Actions

createKbEntry(formData)

Creates knowledge base entry.

async function createKbEntry(formData: {
  title: string
  content: string
  description?: string
  area?: string
  visibility?: string   // 'all_members' | 'board' | 'admin_only'
  is_pinned?: boolean
  tags?: string[]
  icon?: string
}): Promise<{ data?: KnowledgeBase; error?: string }>

Role Required: Member


updateKbEntry(entryId, updates)

Updates knowledge base entry.

async function updateKbEntry(entryId: string, updates: {
  title?: string
  content?: string
  area?: string
  visibility?: string
  is_pinned?: boolean
  tags?: string[]
}): Promise<{ success?: boolean; error?: string }>

Role Required: Member


deleteKbEntry(entryId)

Deletes knowledge base entry.

async function deleteKbEntry(entryId: string): Promise<{ success?: boolean; error?: string }>

Role Required: Board or Admin (via RLS)


createSecret(formData)

Creates encrypted secret.

async function createSecret(formData: {
  title: string
  value: string
  description?: string
  area?: string
  icon?: string
}): Promise<{ data?: Secret; error?: string }>

Role Required: Admin or Board


deleteSecret(secretId)

Deletes secret.

async function deleteSecret(secretId: string): Promise<{ success?: boolean; error?: string }>

Role Required: Admin only


upsertAreaLead(formData)

Creates or updates area lead assignment.

async function upsertAreaLead(formData: {
  area_code: string
  area_name: string
  lead_id?: string
  lead_handle?: string
  status?: string       // 'active' | 'vacant' | 'handoff'
}): Promise<{ data?: AreaLead; error?: string }>

Role Required: Admin or Board

Behavior:

  • Upserts on (space_id, area_code)

8. Contact Actions

createContact(formData)

Creates a contact entry.

async function createContact(formData: {
  name: string
  contact_type: string  // 'vendor' | 'supplier' | 'partner' | 'landlord' | 'city'
  email?: string
  phone?: string
  details?: string
  note?: string
  group_label?: string
  tags?: string[]
}): Promise<{ data?: Contact; error?: string }>

Role Required: Member

Behavior:

  • Auto-generates 3-letter code from name

updateContact(contactId, updates)

Updates contact.

async function updateContact(contactId: string, updates: {
  name?: string
  contact_type?: string
  email?: string
  phone?: string
  details?: string
  note?: string
  group_label?: string
  tags?: string[]
}): Promise<{ success?: boolean; error?: string }>

Role Required: Member


deleteContact(contactId)

Deletes contact.

async function deleteContact(contactId: string): Promise<{ success?: boolean; error?: string }>

Role Required: Member


9. Settings Actions

updateSpaceSettings(updates)

Updates space configuration.

async function updateSpaceSettings(updates: {
  name?: string
  slug?: string
  city?: string
  require_approval?: boolean
  public_member_directory?: boolean
}): Promise<{ success?: boolean; error?: string }>

Role Required: Admin only


saveIntegration(platform, config)

Saves integration configuration.

async function saveIntegration(
  platform: string, 
  config: Record<string, string>
): Promise<{ success?: boolean; error?: string }>

Role Required: Admin only

Behavior:

  • Upserts on (space_id, platform)
  • Stores config with _set indicators for each field

disconnectIntegration(platform)

Disconnects/clears integration.

async function disconnectIntegration(platform: string): Promise<{ success?: boolean; error?: string }>

Role Required: Admin only


rotateWebhookSecret()

Generates new webhook secret.

async function rotateWebhookSecret(): Promise<{ secret?: string; error?: string }>

Role Required: Admin only

Returns: New whsec_ prefixed secret


10. Data Fetching Patterns

Server Component Fetch

// In page.tsx (Server Component)
import { createClient } from '@/lib/supabase/server'

export default async function TasksPage() {
  const supabase = await createClient()
  const { data: { user } } = await supabase.auth.getUser()
  
  const { data: member } = await supabase
    .from('space_members')
    .select('space_id, display_name, role')
    .eq('user_id', user!.id)
    .eq('status', 'current')
    .single()

  const { data: tasks } = await supabase
    .from('tasks')
    .select('*')
    .eq('space_id', member.space_id)
    .order('created_at', { ascending: false })

  return <TasksClient tasks={tasks ?? []} />
}

Parallel Fetching

const [
  { data: tasks },
  { data: projects },
  { count: memberCount }
] = await Promise.all([
  supabase.from('tasks').select('*').eq('space_id', spaceId),
  supabase.from('projects').select('*').eq('space_id', spaceId),
  supabase.from('space_members').select('*', { count: 'exact', head: true }).eq('space_id', spaceId)
])

11. Real-time Subscriptions

Comms Messages

'use client'
import { createClient } from '@/lib/supabase/client'
import { useEffect, useState } from 'react'

function useChannelMessages(channelId: string) {
  const [messages, setMessages] = useState([])

  useEffect(() => {
    const supabase = createClient()
    
    // Initial fetch
    supabase
      .from('comms_messages')
      .select('*')
      .eq('channel_id', channelId)
      .order('created_at')
      .then(({ data }) => setMessages(data ?? []))

    // Subscribe to new messages
    const subscription = supabase
      .channel(`messages:${channelId}`)
      .on('postgres_changes', {
        event: 'INSERT',
        schema: 'public',
        table: 'comms_messages',
        filter: `channel_id=eq.${channelId}`
      }, payload => {
        setMessages(prev => [...prev, payload.new])
      })
      .subscribe()

    return () => {
      subscription.unsubscribe()
    }
  }, [channelId])

  return messages
}

12. Error Handling

Action Return Pattern

All actions return a consistent shape:

// Success with data
{ data: T }

// Success without data
{ success: true }

// Error
{ error: string }

Client-side Handling

const [error, setError] = useState('')
const [loading, setLoading] = useState(false)

async function handleSubmit() {
  setLoading(true)
  setError('')
  
  const result = await createTask(formData)
  
  if (result.error) {
    setError(result.error)
  } else {
    // Success - close modal, refresh data, etc.
  }
  
  setLoading(false)
}

Common Error Messages

Error Cause Solution
"Not authenticated" No session Redirect to login
"No active membership" User has no space Redirect to signup
"Admin access required" Role check failed Show permission error
"Treasurer access required" Payment role check Show permission error
"Invalid invite code" Code not found Ask user to verify
"A space with this URL slug already exists" Duplicate slug Suggest different slug