# 📚 Stafflyn Backend API Documentation

**Version:** 1.0  
**Last Updated:** May 4, 2026  
**Base URL:** `https://backend.stafflyn.com/api` (Production) or `http://localhost:8000/api` (Development)

---

## 📋 Table of Contents

1. [Authentication Endpoints](#authentication-endpoints)
2. [Job Endpoints](#job-endpoints)
3. [Application Endpoints](#application-endpoints)
4. [Notification Endpoints](#notification-endpoints)
5. [Blog Endpoints](#blog-endpoints)
6. [SEO & Schema Endpoints](#seo--schema-endpoints)
7. [Error Handling](#error-handling)
8. [Authentication](#authentication)

---

## 🔐 Authentication

### Token-Based Authentication

All protected endpoints require a Bearer token in the Authorization header:

```http
Authorization: Token YOUR_AUTH_TOKEN
```

**Getting a Token:**

```bash
# After login, you receive a token:
curl -X POST http://localhost:8000/api/auth/login/ \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "password": "password123"
  }'

# Response includes: "token": "abc123xyz..."
```

### Public Endpoints

Some endpoints don't require authentication (marked as **Public**):
- Job listings
- Blog listings
- Schema/SEO endpoints
- User registration
- Email verification
- Password reset

---

## Authentication Endpoints

Base URL: `/api/auth/`

### POST `/api/auth/register/`

**Purpose:** Register a new user account

**Authentication:** Public (Not required)

**HTTP Method:** `POST`

**Request Body:**
```json
{
  "username": "string (required, unique, max 150 chars)",
  "email": "email (required, unique)",
  "password1": "string (required, min 8 chars)",
  "password2": "string (required, must match password1)"
}
```

**Response (201 Created):**
```json
{
  "message": "Registration successful. Please check your email to verify your account.",
  "email": "user@example.com",
  "verification_token": "token-string (for testing only)"
}
```

**Error Responses:**
- `400 Bad Request` - Validation failed (username exists, passwords don't match, etc.)
- `400 Bad Request` - Email already registered

---

### POST `/api/auth/verify-email/`

**Purpose:** Verify user email using token from email

**Authentication:** Public

**HTTP Method:** `POST`

**Request Body:**
```json
{
  "token": "string (required, from verification email)"
}
```

**Response (200 OK):**
```json
{
  "message": "Email verified successfully. You can now login.",
  "user": {
    "id": 1,
    "username": "johnDoe",
    "email": "john@example.com",
    "is_email_verified": true,
    "is_admin": false
  },
  "token": "auth-token-string"
}
```

**Error Responses:**
- `400 Bad Request` - Invalid or expired token
- `404 Not Found` - Token doesn't exist

---

### POST `/api/auth/login/`

**Purpose:** Authenticate user and get auth token

**Authentication:** Public

**HTTP Method:** `POST`

**Request Body:**
```json
{
  "email": "string (required)",
  "password": "string (required)"
}
```

**Response (200 OK):**
```json
{
  "user": {
    "id": 1,
    "username": "johnDoe",
    "email": "john@example.com",
    "is_email_verified": true,
    "is_admin": false
  },
  "token": "auth-token-string",
  "message": "Login successful"
}
```

**Error Responses:**
- `401 Unauthorized` - Invalid email or password
- `400 Bad Request` - Email not verified

---

### POST `/api/auth/logout/`

**Purpose:** Logout and invalidate user token

**Authentication:** Required (Token)

**HTTP Method:** `POST`

**Request Body:** Empty

**Response (200 OK):**
```json
{
  "message": "Logout successful"
}
```

---

### GET `/api/auth/profile/`

**Purpose:** Get current logged-in user's profile

**Authentication:** Required (Token)

**HTTP Method:** `GET`

**Response (200 OK):**
```json
{
  "id": 1,
  "username": "johnDoe",
  "email": "john@example.com",
  "first_name": "John",
  "last_name": "Doe",
  "mobile_number": "+1234567890",
  "role": "employer|candidate",
  "skills": ["Python", "Django", "REST API"],
  "bio": "Experienced developer",
  "experience_years": 5,
  "profile_picture": "https://example.com/media/profile_pics/user_1.jpg",
  "cv": "https://example.com/media/cvs/user_1.pdf",
  "is_email_verified": true,
  "is_admin": false
}
```

---

### PUT `/api/auth/profile/`

**Purpose:** Update user profile (full update)

**Authentication:** Required (Token)

**HTTP Method:** `PUT`

**Request Body (all fields optional):**
```json
{
  "first_name": "string",
  "last_name": "string",
  "mobile_number": "string",
  "role": "employer|candidate",
  "skills": ["string"],
  "bio": "string (max 500 chars)",
  "experience_years": "integer",
  "profile_picture": "file (image)",
  "cv": "file (PDF)"
}
```

**Response (200 OK):**
```json
{
  "message": "Profile updated successfully",
  "profile": {
    "id": 1,
    "username": "johnDoe",
    "first_name": "John",
    "last_name": "Doe",
    "mobile_number": "+1234567890",
    "role": "employer",
    "skills": ["Python", "Django"],
    "bio": "Experienced developer",
    "experience_years": 5,
    "profile_picture": "url",
    "cv": "url"
  }
}
```

---

### PATCH `/api/auth/profile/`

**Purpose:** Partial update user profile

**Authentication:** Required (Token)

**HTTP Method:** `PATCH`

**Request Body (optional fields):**
```json
{
  "first_name": "string",
  "last_name": "string",
  "bio": "string"
}
```

**Response (200 OK):** Updated profile object

---

### POST `/api/auth/forgot-password/`

**Purpose:** Request password reset email

**Authentication:** Public

**HTTP Method:** `POST`

**Request Body:**
```json
{
  "email": "string (required)"
}
```

**Response (200 OK):**
```json
{
  "message": "Password reset email sent. Please check your email.",
  "email": "john@example.com"
}
```

**Error Responses:**
- `404 Not Found` - Email doesn't exist

---

### POST `/api/auth/reset-password/`

**Purpose:** Reset password using token from email

**Authentication:** Public

**HTTP Method:** `POST`

**Request Body:**
```json
{
  "token": "string (required, from reset email)",
  "new_password": "string (required, min 8 chars)",
  "confirm_password": "string (required, must match new_password)"
}
```

**Response (200 OK):**
```json
{
  "message": "Password reset successfully. You can now login with your new password.",
  "email": "john@example.com"
}
```

**Error Responses:**
- `400 Bad Request` - Invalid or expired token
- `400 Bad Request` - Passwords don't match

---

## Job Endpoints

Base URL: `/api/jobs/`

### GET `/api/jobs/`

**Purpose:** List all jobs

**Authentication:** Public

**HTTP Method:** `GET`

**Query Parameters:**
- `page` (optional): integer - Page number for pagination
- `page_size` (optional): integer - Items per page
- `search` (optional): string - Search by title or company name
- `country` (optional): string - Filter by country
- `company` (optional): string - Filter by company name

**Response (200 OK):**
```json
{
  "count": 100,
  "next": "http://localhost:8000/api/jobs/?page=2",
  "previous": null,
  "results": [
    {
      "id": 1,
      "title": "Senior Python Developer",
      "seniority": "Senior",
      "monthlyBudget": "$5000-$7000",
      "Tags": ["Python", "Django", "REST API"],
      "CompanyName": "Tech Corp",
      "CompanyCountry": "USA",
      "workEmail": "careers@techcorp.com",
      "startDate": "2024-06-01",
      "requirements": "5+ years experience...",
      "meta_title": "Senior Python Developer at Tech Corp",
      "meta_description": "Looking for experienced Python developer",
      "status": true,
      "created_at": "2024-05-01T10:00:00Z",
      "updated_at": "2024-05-03T12:00:00Z"
    }
  ]
}
```

---

### POST `/api/jobs/`

**Purpose:** Create a new job listing

**Authentication:** Public

**HTTP Method:** `POST`

**Request Body (for single hire):**
```json
{
  "hiring_type": "single",
  "title": "string (required)",
  "seniority": "string (required)",
  "monthlyBudget": "string (required)",
  "Tags": ["string"],
  "CompanyName": "string (required)",
  "CompanyCountry": "string (required)",
  "workEmail": "email (required)",
  "startDate": "string (required)",
  "requirements": "string (required)",
  "meta_title": "string (optional, 60 chars max)",
  "meta_description": "string (optional, 160 chars max)",
  "og_title": "string (optional)",
  "og_description": "string (optional)",
  "og_image": "file (optional)",
  "og_image_alt": "string (optional)",
  "status": boolean (optional, default: true)
}
```

**Request Body (for multiple positions):**
```json
{
  "hiring_type": "multiple",
  "CompanyName": "string (required)",
  "CompanyCountry": "string (required)",
  "workEmail": "email (required)",
  "startDate": "string (required)",
  "requirements": "string (required)",
  "positions": [
    {
      "title": "string",
      "seniority": "string",
      "monthlyBudget": "string",
      "Tags": ["string"]
    }
  ],
  "status": boolean (optional)
}
```

**Response (201 Created):**
```json
{
  "id": 1,
  "title": "Senior Python Developer",
  "seniority": "Senior",
  "monthlyBudget": "$5000-$7000",
  "Tags": ["Python", "Django"],
  "CompanyName": "Tech Corp",
  "CompanyCountry": "USA",
  "workEmail": "careers@techcorp.com",
  "startDate": "2024-06-01",
  "requirements": "5+ years experience...",
  "status": true,
  "created_at": "2024-05-01T10:00:00Z",
  "updated_at": "2024-05-01T10:00:00Z"
}
```

**Error Responses:**
- `400 Bad Request` - Validation failed
- `400 Bad Request` - Multiple positions format error

---

### GET `/api/jobs/{id}/`

**Purpose:** Get a specific job by ID

**Authentication:** Public

**HTTP Method:** `GET`

**URL Parameters:**
- `id` (required): integer - Job ID

**Response (200 OK):** Job object (same as above)

**Error Responses:**
- `404 Not Found` - Job doesn't exist

---

### PUT `/api/jobs/{id}/`

**Purpose:** Update a job (full replacement)

**Authentication:** Required (Admin only)

**HTTP Method:** `PUT`

**Request Body:** Same as POST

**Response (200 OK):** Updated job object

**Error Responses:**
- `403 Forbidden` - Not admin
- `404 Not Found` - Job doesn't exist

---

### PATCH `/api/jobs/{id}/`

**Purpose:** Partial update a job

**Authentication:** Required (Admin only)

**HTTP Method:** `PATCH`

**Request Body (optional fields):**
```json
{
  "title": "string",
  "status": boolean,
  "requirements": "string"
}
```

**Response (200 OK):** Updated job object

---

### DELETE `/api/jobs/{id}/`

**Purpose:** Delete a job

**Authentication:** Required (Admin only)

**HTTP Method:** `DELETE`

**Response (204 No Content):** Empty response

---

### GET `/api/jobs/by_company/`

**Purpose:** Filter jobs by company name

**Authentication:** Public

**HTTP Method:** `GET`

**Query Parameters:**
- `company_name` (required): string - Company name to filter

**Response (200 OK):** Array of job objects

---

### GET `/api/jobs/by_country/`

**Purpose:** Filter jobs by country

**Authentication:** Public

**HTTP Method:** `GET`

**Query Parameters:**
- `country` (required): string - Country name to filter

**Response (200 OK):** Array of job objects

---

## Application Endpoints

Base URL: `/api/applications/`

### GET `/api/applications/`

**Purpose:** List job applications

**Authentication:** Required (Token)

**HTTP Method:** `GET`

**Query Parameters:**
- `page` (optional): integer - Page number
- `status` (optional): string - Filter by status (applied, interview, rejected, offered, withdrawn)

**Response (200 OK):**
```json
[
  {
    "id": 1,
    "username": "johnDoe",
    "email": "john@example.com",
    "job": 1,
    "job_details": {
      "id": 1,
      "title": "Senior Python Developer",
      "CompanyName": "Tech Corp"
    },
    "status": "applied",
    "applied_at": "2024-05-03T10:00:00Z",
    "updated_at": "2024-05-03T10:00:00Z"
  }
]
```

---

### POST `/api/applications/`

**Purpose:** Apply for a job

**Authentication:** Required (Token)

**HTTP Method:** `POST`

**Request Body:**
```json
{
  "job": "integer (required, job ID)"
}
```

**Response (201 Created):**
```json
{
  "id": 1,
  "username": "johnDoe",
  "email": "john@example.com",
  "job": 1,
  "job_details": { ... },
  "status": "applied",
  "applied_at": "2024-05-03T10:00:00Z",
  "updated_at": "2024-05-03T10:00:00Z"
}
```

**Error Responses:**
- `404 Not Found` - Job doesn't exist
- `400 Bad Request` - Already applied for this job

---

### GET `/api/applications/{id}/`

**Purpose:** Get a specific application

**Authentication:** Required (Token)

**HTTP Method:** `GET`

**Response (200 OK):** Application object

**Error Responses:**
- `404 Not Found` - Application doesn't exist
- `403 Forbidden` - Not your application (non-admin)

---

### PUT `/api/applications/{id}/`

**Purpose:** Update application (full replacement)

**Authentication:** Required (Token)

**HTTP Method:** `PUT`

**Request Body:**
```json
{
  "job": integer,
  "status": "applied|interview|rejected|offered|withdrawn"
}
```

**Response (200 OK):** Updated application

---

### PATCH `/api/applications/{id}/`

**Purpose:** Partial update application

**Authentication:** Required (Token)

**HTTP Method:** `PATCH`

**Request Body (optional):**
```json
{
  "status": "applied|interview|rejected|offered|withdrawn"
}
```

**Response (200 OK):** Updated application

---

### DELETE `/api/applications/{id}/`

**Purpose:** Delete application

**Authentication:** Required (Token)

**HTTP Method:** `DELETE`

**Response (204 No Content)**

---

### GET `/api/applications/my_applications/`

**Purpose:** Get current user's job applications

**Authentication:** Required (Token)

**HTTP Method:** `GET`

**Response (200 OK):** Array of application objects

---

### GET `/api/applications/job_applications/`

**Purpose:** Get applications for a specific job (employer/admin only)

**Authentication:** Required (Token)

**HTTP Method:** `GET`

**Query Parameters:**
- `job_id` (required): integer - Job ID

**Response (200 OK):** Array of application objects for the job

**Error Responses:**
- `403 Forbidden` - Not the job employer

---

### PATCH `/api/applications/{id}/update_status/`

**Purpose:** Update application status

**Authentication:** Required (Token)

**HTTP Method:** `PATCH`

**Request Body:**
```json
{
  "status": "applied|interview|rejected|offered|withdrawn"
}
```

**Response (200 OK):** Updated application

---

### POST `/api/applications/{id}/withdraw/`

**Purpose:** Withdraw job application (candidate only)

**Authentication:** Required (Token)

**HTTP Method:** `POST`

**Request Body:** Empty

**Response (200 OK):**
```json
{
  "id": 1,
  "status": "withdrawn",
  "message": "Application withdrawn successfully"
}
```

---

## Notification Endpoints

Base URL: `/api/notifications/`

### GET `/api/notifications/`

**Purpose:** List user's notifications

**Authentication:** Required (Token)

**HTTP Method:** `GET`

**Query Parameters:**
- `page` (optional): integer - Page number
- `is_read` (optional): boolean - Filter by read status

**Response (200 OK):**
```json
[
  {
    "id": 1,
    "notification_type": "job_posting|application_received|status_changed",
    "title": "New Job Posted",
    "message": "A new job matching your skills has been posted",
    "job_id": 1,
    "application_id": null,
    "is_read": false,
    "created_at": "2024-05-03T10:00:00Z",
    "updated_at": "2024-05-03T10:00:00Z"
  }
]
```

---

### POST `/api/notifications/`

**Purpose:** Create notification (admin only)

**Authentication:** Required (Token)

**HTTP Method:** `POST`

**Request Body:**
```json
{
  "notification_type": "string",
  "title": "string (required)",
  "message": "string (required)",
  "job_id": "integer (optional)",
  "application_id": "integer (optional)"
}
```

**Response (201 Created):** Notification object

---

### GET `/api/notifications/{id}/`

**Purpose:** Get a specific notification

**Authentication:** Required (Token)

**HTTP Method:** `GET`

**Response (200 OK):** Notification object

---

### GET `/api/notifications/unread_count/`

**Purpose:** Get count of unread notifications

**Authentication:** Required (Token)

**HTTP Method:** `GET`

**Response (200 OK):**
```json
{
  "unread_count": 5
}
```

---

### GET `/api/notifications/unread/`

**Purpose:** Get all unread notifications

**Authentication:** Required (Token)

**HTTP Method:** `GET`

**Response (200 OK):** Array of unread notification objects

---

### POST `/api/notifications/{id}/mark_as_read/`

**Purpose:** Mark notification as read

**Authentication:** Required (Token)

**HTTP Method:** `POST`

**Request Body:** Empty

**Response (200 OK):** Updated notification with is_read=true

---

### POST `/api/notifications/{id}/mark_as_unread/`

**Purpose:** Mark notification as unread

**Authentication:** Required (Token)

**HTTP Method:** `POST`

**Response (200 OK):** Updated notification with is_read=false

---

### POST `/api/notifications/mark_all_as_read/`

**Purpose:** Mark all notifications as read

**Authentication:** Required (Token)

**HTTP Method:** `POST`

**Request Body:** Empty

**Response (200 OK):**
```json
{
  "message": "5 notification(s) marked as read"
}
```

---

### DELETE `/api/notifications/clear_all/`

**Purpose:** Delete all notifications

**Authentication:** Required (Token)

**HTTP Method:** `DELETE`

**Response (200 OK):**
```json
{
  "message": "5 notification(s) deleted"
}
```

---

## Blog Endpoints

Base URL: `/api/blogs/`

### GET `/api/blogs/`

**Purpose:** List all blogs (published for public, all for admin)

**Authentication:** Public

**HTTP Method:** `GET`

**Query Parameters:**
- `page` (optional): integer - Page number
- `page_size` (optional): integer - Items per page
- `is_published` (optional): boolean - Filter by publish status
- `search` (optional): string - Search by title or content

**Response (200 OK):**
```json
{
  "count": 50,
  "next": "url",
  "previous": null,
  "results": [
    {
      "slug": "how-to-learn-django",
      "title": "How to Learn Django",
      "content": "Complete guide to learning Django...",
      "thumbnail_image": "url",
      "thumbnail_image_alt": "Django logo",
      "featured_image": "url",
      "featured_image_alt": "Featured image",
      "meta_title": "Learn Django - Complete Guide",
      "meta_description": "Comprehensive guide to learning Django framework",
      "tags": "python,django,web development",
      "og_title": "Learn Django",
      "og_description": "Complete guide",
      "og_image": "url",
      "og_image_alt": "OG image",
      "author": "admin",
      "is_featured": true,
      "is_published": true,
      "word_count": 2500,
      "reading_time": 12,
      "created_at": "2024-05-01T10:00:00Z",
      "updated_at": "2024-05-03T12:00:00Z",
      "published_at": "2024-05-01T10:00:00Z"
    }
  ]
}
```

---

### POST `/api/blogs/`

**Purpose:** Create a new blog post (admin only)

**Authentication:** Required (Token, Admin)

**HTTP Method:** `POST`

**Request Body:**
```json
{
  "title": "string (required, unique)",
  "content": "string (required)",
  "thumbnail_image": "file (required, image)",
  "thumbnail_image_alt": "string (required)",
  "featured_image": "file (required, image)",
  "featured_image_alt": "string (required)",
  "meta_title": "string (required, max 60 chars)",
  "meta_description": "string (required, max 160 chars)",
  "tags": "string (comma-separated)",
  "og_title": "string (optional)",
  "og_description": "string (optional)",
  "og_image": "file (optional)",
  "og_image_alt": "string (optional)",
  "is_featured": boolean (optional),
  "is_published": boolean (optional)
}
```

**Response (201 Created):**
```json
{
  "slug": "how-to-learn-django",
  "title": "How to Learn Django",
  "content": "Complete guide...",
  "thumbnail_image": "url",
  "featured_image": "url",
  "meta_title": "Learn Django",
  "meta_description": "Guide",
  "tags": "python,django",
  "word_count": 2500,
  "reading_time": 12,
  "is_published": false
}
```

---

### GET `/api/blogs/{slug}/`

**Purpose:** Get a specific blog post

**Authentication:** Public

**HTTP Method:** `GET`

**URL Parameters:**
- `slug` (required): string - Blog slug

**Response (200 OK):** Blog object

---

### PUT `/api/blogs/{slug}/`

**Purpose:** Update blog post (full replacement, admin only)

**Authentication:** Required (Token, Admin)

**HTTP Method:** `PUT`

**Request Body:** Same as POST

**Response (200 OK):** Updated blog object

---

### PATCH `/api/blogs/{slug}/`

**Purpose:** Partial update blog post (admin only)

**Authentication:** Required (Token, Admin)

**HTTP Method:** `PATCH`

**Request Body (optional fields):**
```json
{
  "title": "string",
  "content": "string",
  "is_published": boolean,
  "tags": "string"
}
```

**Response (200 OK):** Updated blog object

---

### DELETE `/api/blogs/{slug}/`

**Purpose:** Delete blog post (admin only)

**Authentication:** Required (Token, Admin)

**HTTP Method:** `DELETE`

**Response (204 No Content)**

---

## SEO & Schema Endpoints

Base URL: `/api/`

### GET `/api/sitemap/`

**Purpose:** Get sitemap data in JSON format

**Authentication:** Public

**HTTP Method:** `GET`

**Response (200 OK):**
```json
{
  "frontend_base_url": "https://stafflyn.com",
  "total_urls": 152,
  "urls": [
    {
      "loc": "https://stafflyn.com/",
      "priority": 1.0,
      "changefreq": "daily",
      "lastmod": null
    },
    {
      "loc": "https://stafflyn.com/jobs/",
      "priority": 0.9,
      "changefreq": "daily",
      "lastmod": null
    },
    {
      "loc": "https://stafflyn.com/jobs/123/",
      "priority": 0.8,
      "changefreq": "weekly",
      "lastmod": "2024-05-03T12:00:00Z"
    },
    {
      "loc": "https://stafflyn.com/blogs/",
      "priority": 0.9,
      "changefreq": "daily",
      "lastmod": null
    },
    {
      "loc": "https://stafflyn.com/blogs/how-to-learn-django/",
      "priority": 0.7,
      "changefreq": "weekly",
      "lastmod": "2024-05-03T10:00:00Z"
    }
  ]
}
```

---

### GET `/api/schema/blog/{slug}/`

**Purpose:** Get JSON-LD schema markup for a blog post

**Authentication:** Public

**HTTP Method:** `GET`

**URL Parameters:**
- `slug` (required): string - Blog slug

**Response (200 OK):**
```json
{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "How to Learn Django",
  "description": "Comprehensive guide to learning Django framework",
  "image": {
    "@type": "ImageObject",
    "url": "https://stafflyn.com/media/blog_images/image.jpg",
    "width": 1200,
    "height": 630,
    "name": "Featured image"
  },
  "datePublished": "2024-05-01T10:00:00Z",
  "dateModified": "2024-05-03T12:00:00Z",
  "author": {
    "@type": "Person",
    "name": "Admin User"
  },
  "mainEntity": {
    "@type": "Article",
    "wordCount": 2500,
    "timeRequired": "PT12M"
  },
  "keywords": "python,django,web development"
}
```

**Error Responses:**
- `404 Not Found` - Blog doesn't exist

---

### GET `/api/schema/job/{slug}/`

**Purpose:** Get JSON-LD schema markup for a job posting

**Authentication:** Public

**HTTP Method:** `GET`

**URL Parameters:**
- `job_id` (required): integer - Job ID

**Response (200 OK):**
```json
{
  "@context": "https://schema.org",
  "@type": "JobPosting",
  "title": "Senior Python Developer",
  "description": "5+ years experience required...",
  "hiringOrganization": {
    "@type": "Organization",
    "name": "Tech Corp",
    "contactPoint": {
      "@type": "ContactPoint",
      "contactType": "Recruiting",
      "email": "careers@techcorp.com"
    }
  },
  "jobLocation": {
    "@type": "Place",
    "address": {
      "@type": "PostalAddress",
      "addressCountry": "USA"
    }
  },
  "baseSalary": {
    "@type": "PriceSpecification",
    "priceCurrency": "USD",
    "price": "5000"
  },
  "datePosted": "2024-05-01T10:00:00Z",
  "jobLocationType": "TELECOMMUTE",
  "skills": ["Python", "Django", "REST API"]
}
```

---

### GET `/api/schema/organization/`

**Purpose:** Get JSON-LD schema markup for the organization

**Authentication:** Public

**HTTP Method:** `GET`

**Response (200 OK):**
```json
{
  "@context": "https://schema.org",
  "@type": "Organization",
  "name": "Stafflyn",
  "url": "https://stafflyn.com",
  "description": "A platform connecting employers with talented professionals",
  "logo": "https://stafflyn.com/logo.png",
  "sameAs": [
    "https://www.facebook.com/stafflyn",
    "https://twitter.com/stafflyn"
  ],
  "contactPoint": {
    "@type": "ContactPoint",
    "contactType": "Customer Service",
    "email": "support@stafflyn.com"
  }
}
```

---

### GET `/api/schema/breadcrumb/`

**Purpose:** Get JSON-LD schema markup for breadcrumb navigation

**Authentication:** Public

**HTTP Method:** `GET`

**Query Parameters:**
- `path` (required): string - URL path (e.g., `/blogs/how-to-learn-django/`)

**Response (200 OK):**
```json
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "name": "Home",
      "item": "https://stafflyn.com"
    },
    {
      "@type": "ListItem",
      "position": 2,
      "name": "Blogs",
      "item": "https://stafflyn.com/blogs"
    },
    {
      "@type": "ListItem",
      "position": 3,
      "name": "How to Learn Django"
    }
  ]
}
```

---

## Error Handling

### Error Response Format

All errors follow this format:

```json
{
  "error": "Error message",
  "detail": "Detailed explanation (optional)",
  "code": "error_code (optional)"
}
```

### Common HTTP Status Codes

| Code | Meaning | Description |
|------|---------|-------------|
| 200 | OK | Request succeeded |
| 201 | Created | Resource created successfully |
| 204 | No Content | Successful deletion |
| 400 | Bad Request | Invalid input or validation failed |
| 401 | Unauthorized | Authentication required or invalid token |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource doesn't exist |
| 409 | Conflict | Resource already exists |
| 500 | Server Error | Internal server error |

### Common Error Responses

**401 Unauthorized:**
```json
{
  "detail": "Authentication credentials were not provided."
}
```

**403 Forbidden:**
```json
{
  "detail": "You do not have permission to perform this action."
}
```

**404 Not Found:**
```json
{
  "detail": "Not found."
}
```

**400 Bad Request:**
```json
{
  "field_name": ["Error message"],
  "another_field": ["Another error"]
}
```

---

## Rate Limiting

API calls are not currently rate-limited but may be in future versions.

---

## Pagination

List endpoints support pagination:

```json
{
  "count": 100,
  "next": "http://localhost:8000/api/jobs/?page=2",
  "previous": null,
  "results": [...]
}
```

**Query Parameters:**
- `page`: Page number (default: 1)
- `page_size`: Items per page (default: 20)

---

## Filtering & Search

Most list endpoints support filtering:

```
GET /api/jobs/?country=USA&page=2
GET /api/blogs/?search=django&is_published=true
GET /api/applications/?status=interview
```

---

## File Upload

For endpoints that accept file uploads (images, PDF):

- **Method:** Multipart form-data
- **Max file size:** 5MB
- **Supported formats:** 
  - Images: JPG, PNG, GIF, WebP
  - Documents: PDF

**Example:**
```bash
curl -X POST http://localhost:8000/api/auth/profile/ \
  -H "Authorization: Token YOUR_TOKEN" \
  -F "profile_picture=@/path/to/image.jpg" \
  -F "bio=My bio text"
```

---

## Examples

### Complete Login Flow

```bash
# 1. Register
curl -X POST http://localhost:8000/api/auth/register/ \
  -H "Content-Type: application/json" \
  -d '{
    "username": "johndoe",
    "email": "john@example.com",
    "password1": "securepass123",
    "password2": "securepass123"
  }'

# 2. Verify email (use token from email)
curl -X POST http://localhost:8000/api/auth/verify-email/ \
  -H "Content-Type: application/json" \
  -d '{"token": "verification_token"}'

# 3. Login
curl -X POST http://localhost:8000/api/auth/login/ \
  -H "Content-Type: application/json" \
  -d '{
    "email": "john@example.com",
    "password": "securepass123"
  }'

# Response includes auth token
```

### Using Auth Token

```bash
curl -X GET http://localhost:8000/api/auth/profile/ \
  -H "Authorization: Token abc123xyz..."
```

### Apply for a Job

```bash
curl -X POST http://localhost:8000/api/applications/ \
  -H "Authorization: Token abc123xyz..." \
  -H "Content-Type: application/json" \
  -d '{"job": 1}'
```

---

## Support & Troubleshooting

### Issue: Invalid Token

**Solution:** Token may be expired or invalid. Login again to get a new token.

### Issue: 403 Forbidden on Admin Endpoints

**Solution:** Only admin users can access these endpoints. Contact support to enable admin access.

### Issue: File Upload Fails

**Solution:** Ensure file size is under 5MB and format is supported.

---

**API Documentation Version:** 1.0  
**Last Updated:** May 4, 2026  
**Maintained By:** Stafflyn Backend Team
