Skip to Content

Jobs

Public job listings plus employer-only create, update, visibility, and apply flows.

List public jobs

Lists published, non-deleted jobs. Supports search, filtering, and pagination query parameters.

GET/api/jobs

Auth: No auth required.

Response body

{
  "data": [
    {
      "id": "22222222-2222-2222-2222-222222222222",
      "company_id": "11111111-1111-1111-1111-111111111111",
      "title": "Frontend student developer",
      "description": "Help build user-facing product features.",
      "location": "Ljubljana",
      "work_type": "hybrid",
      "required_title": "What we are looking for",
      "pay_from": 12,
      "pay_to": 18,
      "pay_type": "hourly",
      "pay_category": "gross",
      "job_type_id": "66666666-6666-6666-6666-666666666666",
      "region_id": "55555555-5555-5555-5555-555555555555",
      "education_level_id": "77777777-7777-7777-7777-777777777777",
      "required_skills": [
        "TypeScript",
        "React"
      ],
      "sections": [
        {
          "type": "markdown",
          "title": "About the role",
          "content": "Build and ship product improvements."
        },
        {
          "type": "required_skills",
          "title": "Required skills",
          "content": null
        }
      ],
      "published_at": "2026-03-28T18:00:00.000Z",
      "deleted_at": null
    }
  ],
  "page": 1,
  "pageSize": 20,
  "totalPages": 1,
  "nextPage": false,
  "prevPage": false
}

Status codes

  • 200 OK
  • 400 Bad Request — Returned when a query value fails schema validation.
  • 500 Internal Server Error

Error format

{
  "error": "Unauthorized",
  "code": "unauthorized"
}

Routes use the shared JSON error envelope; exact `code` values depend on the endpoint.

Create job

Creates a job for a company owned by the authenticated user.

POST/api/jobs

Auth: Bearer token or Clerk session.

Request body

{
  "company_id": "11111111-1111-1111-1111-111111111111",
  "title": "Frontend student developer",
  "description": "Help build user-facing product features.",
  "location": "Ljubljana",
  "work_type": "hybrid",
  "required_title": "What we are looking for",
  "pay_from": 12,
  "pay_to": 18,
  "pay_type": "hourly",
  "pay_category": "gross",
  "job_type_id": "66666666-6666-6666-6666-666666666666",
  "region_id": "55555555-5555-5555-5555-555555555555",
  "education_level_id": "77777777-7777-7777-7777-777777777777",
  "required_skills": [
    "TypeScript",
    "React"
  ],
  "sections": [
    {
      "type": "markdown",
      "title": "About the role",
      "content": "Build and ship product improvements."
    },
    {
      "type": "required_skills",
      "title": "Required skills",
      "content": null
    }
  ]
}

Response body

{
  "id": "22222222-2222-2222-2222-222222222222",
  "company_id": "11111111-1111-1111-1111-111111111111",
  "title": "Frontend student developer",
  "description": "Help build user-facing product features.",
  "location": "Ljubljana",
  "work_type": "hybrid",
  "required_title": "What we are looking for",
  "pay_from": 12,
  "pay_to": 18,
  "pay_type": "hourly",
  "pay_category": "gross",
  "job_type_id": "66666666-6666-6666-6666-666666666666",
  "region_id": "55555555-5555-5555-5555-555555555555",
  "education_level_id": "77777777-7777-7777-7777-777777777777",
  "required_skills": [
    "TypeScript",
    "React"
  ],
  "sections": [
    {
      "type": "markdown",
      "title": "About the role",
      "content": "Build and ship product improvements."
    },
    {
      "type": "required_skills",
      "title": "Required skills",
      "content": null
    }
  ],
  "published_at": "2026-03-28T18:00:00.000Z",
  "deleted_at": null
}

Status codes

  • 201 Created
  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 500 Internal Server Error

Error format

{
  "error": "Unauthorized",
  "code": "unauthorized"
}

Routes use the shared JSON error envelope; exact `code` values depend on the endpoint.

List employer jobs

Lists jobs for the authenticated employer company, including unpublished or deleted rows when present.

GET/api/jobs/private

Auth: Bearer token or Clerk session.

Response body

{
  "data": [
    {
      "id": "22222222-2222-2222-2222-222222222222",
      "company_id": "11111111-1111-1111-1111-111111111111",
      "title": "Frontend student developer",
      "description": "Help build user-facing product features.",
      "location": "Ljubljana",
      "work_type": "hybrid",
      "required_title": "What we are looking for",
      "pay_from": 12,
      "pay_to": 18,
      "pay_type": "hourly",
      "pay_category": "gross",
      "job_type_id": "66666666-6666-6666-6666-666666666666",
      "region_id": "55555555-5555-5555-5555-555555555555",
      "education_level_id": "77777777-7777-7777-7777-777777777777",
      "required_skills": [
        "TypeScript",
        "React"
      ],
      "sections": [
        {
          "type": "markdown",
          "title": "About the role",
          "content": "Build and ship product improvements."
        },
        {
          "type": "required_skills",
          "title": "Required skills",
          "content": null
        }
      ],
      "published_at": "2026-03-28T18:00:00.000Z",
      "deleted_at": null
    }
  ],
  "page": 1,
  "pageSize": 20,
  "totalPages": 1,
  "nextPage": false,
  "prevPage": false
}

Status codes

  • 200 OK
  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 500 Internal Server Error

Error format

{
  "error": "Unauthorized",
  "code": "unauthorized"
}

Routes use the shared JSON error envelope; exact `code` values depend on the endpoint.

Get job

Returns a published job publicly. Owners can also fetch their own unpublished or deleted job.

GET/api/jobs/{id}

Auth: Public for published jobs. Owner auth is optional for private or deleted jobs.

Response body

{
  "id": "22222222-2222-2222-2222-222222222222",
  "company_id": "11111111-1111-1111-1111-111111111111",
  "title": "Frontend student developer",
  "description": "Help build user-facing product features.",
  "location": "Ljubljana",
  "work_type": "hybrid",
  "required_title": "What we are looking for",
  "pay_from": 12,
  "pay_to": 18,
  "pay_type": "hourly",
  "pay_category": "gross",
  "job_type_id": "66666666-6666-6666-6666-666666666666",
  "region_id": "55555555-5555-5555-5555-555555555555",
  "education_level_id": "77777777-7777-7777-7777-777777777777",
  "required_skills": [
    "TypeScript",
    "React"
  ],
  "sections": [
    {
      "type": "markdown",
      "title": "About the role",
      "content": "Build and ship product improvements."
    },
    {
      "type": "required_skills",
      "title": "Required skills",
      "content": null
    }
  ],
  "published_at": "2026-03-28T18:00:00.000Z",
  "deleted_at": null
}

Status codes

  • 200 OK
  • 400 Bad Request
  • 404 Not Found
  • 500 Internal Server Error

Error format

{
  "error": "Unauthorized",
  "code": "unauthorized"
}

Routes use the shared JSON error envelope; exact `code` values depend on the endpoint.

Update job

Updates a non-deleted job owned by the authenticated company.

PATCH/api/jobs/{id}

Auth: Bearer token or Clerk session.

Request body

{
  "title": "Frontend student developer",
  "description": "Help build user-facing product features.",
  "location": "Ljubljana",
  "work_type": "hybrid",
  "required_title": "What we are looking for",
  "pay_from": 12,
  "pay_to": 18,
  "pay_type": "hourly",
  "pay_category": "gross",
  "job_type_id": "66666666-6666-6666-6666-666666666666",
  "region_id": "55555555-5555-5555-5555-555555555555",
  "education_level_id": "77777777-7777-7777-7777-777777777777",
  "required_skills": [
    "TypeScript",
    "React"
  ],
  "sections": [
    {
      "type": "markdown",
      "title": "About the role",
      "content": "Build and ship product improvements."
    },
    {
      "type": "required_skills",
      "title": "Required skills",
      "content": null
    }
  ]
}

Response body

{
  "id": "22222222-2222-2222-2222-222222222222",
  "company_id": "11111111-1111-1111-1111-111111111111",
  "title": "Frontend student developer",
  "description": "Help build user-facing product features.",
  "location": "Ljubljana",
  "work_type": "hybrid",
  "required_title": "What we are looking for",
  "pay_from": 12,
  "pay_to": 18,
  "pay_type": "hourly",
  "pay_category": "gross",
  "job_type_id": "66666666-6666-6666-6666-666666666666",
  "region_id": "55555555-5555-5555-5555-555555555555",
  "education_level_id": "77777777-7777-7777-7777-777777777777",
  "required_skills": [
    "TypeScript",
    "React"
  ],
  "sections": [
    {
      "type": "markdown",
      "title": "About the role",
      "content": "Build and ship product improvements."
    },
    {
      "type": "required_skills",
      "title": "Required skills",
      "content": null
    }
  ],
  "published_at": "2026-03-28T18:00:00.000Z",
  "deleted_at": null
}

Status codes

  • 200 OK
  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 500 Internal Server Error

Error format

{
  "error": "Unauthorized",
  "code": "unauthorized"
}

Routes use the shared JSON error envelope; exact `code` values depend on the endpoint.

Delete job

Soft-deletes a job by default. Add `?hard=true` to permanently delete it.

DELETE/api/jobs/{id}

Auth: Bearer token or Clerk session.

Response body

No response body.

Status codes

  • 204 No Content
  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 500 Internal Server Error

Error format

{
  "error": "Unauthorized",
  "code": "unauthorized"
}

Routes use the shared JSON error envelope; exact `code` values depend on the endpoint.

Publish job

Sets `published_at` for a company-owned job. Publishing can fail if the subscription limit is reached.

POST/api/jobs/{id}/publish

Auth: Bearer token or Clerk session.

Response body

{
  "id": "22222222-2222-2222-2222-222222222222",
  "company_id": "11111111-1111-1111-1111-111111111111",
  "title": "Frontend student developer",
  "description": "Help build user-facing product features.",
  "location": "Ljubljana",
  "work_type": "hybrid",
  "required_title": "What we are looking for",
  "pay_from": 12,
  "pay_to": 18,
  "pay_type": "hourly",
  "pay_category": "gross",
  "job_type_id": "66666666-6666-6666-6666-666666666666",
  "region_id": "55555555-5555-5555-5555-555555555555",
  "education_level_id": "77777777-7777-7777-7777-777777777777",
  "required_skills": [
    "TypeScript",
    "React"
  ],
  "sections": [
    {
      "type": "markdown",
      "title": "About the role",
      "content": "Build and ship product improvements."
    },
    {
      "type": "required_skills",
      "title": "Required skills",
      "content": null
    }
  ],
  "published_at": "2026-03-28T18:00:00.000Z",
  "deleted_at": null
}

Status codes

  • 200 OK
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 500 Internal Server Error

Error format

{
  "error": "Unauthorized",
  "code": "unauthorized"
}

Routes use the shared JSON error envelope; exact `code` values depend on the endpoint.

Unpublish job

Clears `published_at` for a company-owned job.

POST/api/jobs/{id}/unpublish

Auth: Bearer token or Clerk session.

Response body

{
  "id": "22222222-2222-2222-2222-222222222222",
  "company_id": "11111111-1111-1111-1111-111111111111",
  "title": "Frontend student developer",
  "description": "Help build user-facing product features.",
  "location": "Ljubljana",
  "work_type": "hybrid",
  "required_title": "What we are looking for",
  "pay_from": 12,
  "pay_to": 18,
  "pay_type": "hourly",
  "pay_category": "gross",
  "job_type_id": "66666666-6666-6666-6666-666666666666",
  "region_id": "55555555-5555-5555-5555-555555555555",
  "education_level_id": "77777777-7777-7777-7777-777777777777",
  "required_skills": [
    "TypeScript",
    "React"
  ],
  "sections": [
    {
      "type": "markdown",
      "title": "About the role",
      "content": "Build and ship product improvements."
    },
    {
      "type": "required_skills",
      "title": "Required skills",
      "content": null
    }
  ],
  "published_at": "2026-03-28T18:00:00.000Z",
  "deleted_at": null
}

Status codes

  • 200 OK
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 500 Internal Server Error

Error format

{
  "error": "Unauthorized",
  "code": "unauthorized"
}

Routes use the shared JSON error envelope; exact `code` values depend on the endpoint.

Apply for job (form flow)

Submits a form-based application attempt and redirects back to the job page. This route does not return JSON.

POST/api/jobs/{id}/apply

Auth: Bearer token or Clerk session for a student account.

Request body

Content-Type: multipart/form-data

jobId: "22222222-2222-2222-2222-222222222222"

The route reads multipart form data and requires the `jobId` field to match the path.

Response body

Redirect target examples:
/sl/app/jobs/22222222-2222-2222-2222-222222222222?success=true
/sl/app/jobs/22222222-2222-2222-2222-222222222222?error=validation
/sl/app/jobs/22222222-2222-2222-2222-222222222222?error=application_failed

The route redirects to the default locale job page instead of returning JSON.

Status codes

  • 307 Temporary Redirect — The route calls `NextResponse.redirect()` without an explicit status.

Error format

No JSON error body. Failures are represented by redirect query parameters.