docs: add API documentation with routes and permissions
- Create docs/API.md with complete API reference - 40+ routes documented - Permission matrix by role - Module breakdown (Auth, Users, Jobs, Applications, etc.) - Error response examples - ID format documentation - Fix seeders to use SERIAL (not UUID) for jobs.id - jobs.js: let DB auto-generate id - acme.js, fictional-companies.js, epic-companies.js: same fix - Update README.md with API documentation link
This commit is contained in:
parent
ae4a3e5e63
commit
7f30a214f7
6 changed files with 499 additions and 19 deletions
|
|
@ -223,6 +223,7 @@ Veja a documentação completa do banco de dados em: [docs/DATABASE.md](docs/DAT
|
|||
### Roadmap & Tarefas
|
||||
- 🗺️ **Roadmap:** [docs/ROADMAP.md](docs/ROADMAP.md) - Status e progresso do projeto
|
||||
- 📋 **Tarefas:** [docs/TASKS.md](docs/TASKS.md) - Lista detalhada para evitar retrabalho
|
||||
- 📡 **API:** [docs/API.md](docs/API.md) - Documentação completa com rotas e permissões
|
||||
|
||||
### Documentação por Componente
|
||||
| Componente | Documentação |
|
||||
|
|
|
|||
480
docs/API.md
Normal file
480
docs/API.md
Normal file
|
|
@ -0,0 +1,480 @@
|
|||
# 📡 GoHorse Jobs - API Documentation
|
||||
|
||||
Complete API reference with routes, permissions, and modules.
|
||||
|
||||
> **Last Updated:** 2024-12-24
|
||||
> **Base URL:** `http://localhost:8521/api/v1`
|
||||
> **Auth:** JWT Bearer Token or HttpOnly Cookie
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Authentication
|
||||
|
||||
### Methods
|
||||
1. **Authorization Header:** `Authorization: Bearer <token>`
|
||||
2. **Cookie:** `jwt=<token>` (HttpOnly, Secure)
|
||||
|
||||
### Roles
|
||||
| Role | Code | Level | Description |
|
||||
|------|------|-------|-------------|
|
||||
| **SuperAdmin** | `superadmin` | 0 | Platform administrator |
|
||||
| **CompanyAdmin** | `companyAdmin` | 1 | Company administrator |
|
||||
| **Recruiter** | `recruiter` | 2 | Job poster |
|
||||
| **JobSeeker** | `jobSeeker` | 3 | Candidate |
|
||||
| **Guest** | - | - | No authentication |
|
||||
|
||||
---
|
||||
|
||||
## 📋 Module: Authentication
|
||||
|
||||
### Login
|
||||
```http
|
||||
POST /api/v1/auth/login
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Public | ❌ | Guest | Authenticate user |
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"identifier": "superadmin",
|
||||
"password": "Admin@2025!"
|
||||
}
|
||||
```
|
||||
|
||||
**Response (200):**
|
||||
```json
|
||||
{
|
||||
"token": "eyJhbGciOiJI...",
|
||||
"user": { "id": 1, "role": "superadmin", "name": "Super Administrator" }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Register Candidate
|
||||
```http
|
||||
POST /api/v1/auth/register
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Public | ❌ | Guest | Register new job seeker |
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"name": "John Doe",
|
||||
"email": "john@example.com",
|
||||
"password": "SecurePass123!"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🏢 Module: Companies
|
||||
|
||||
### Create Company
|
||||
```http
|
||||
POST /api/v1/companies
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Public | ❌ | Guest | Register new company |
|
||||
|
||||
### List Companies
|
||||
```http
|
||||
GET /api/v1/companies
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Smart | ⚪ Optional | Guest/Admin | Public list or admin full list |
|
||||
|
||||
### Update Company Status
|
||||
```http
|
||||
PATCH /api/v1/companies/{id}/status
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | superadmin, admin | Activate/deactivate company |
|
||||
|
||||
---
|
||||
|
||||
## 👥 Module: Users
|
||||
|
||||
### Get Current User
|
||||
```http
|
||||
GET /api/v1/users/me
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | Any authenticated | Get current user profile |
|
||||
|
||||
### List Users
|
||||
```http
|
||||
GET /api/v1/users
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | superadmin, admin | List all users |
|
||||
|
||||
### Create User
|
||||
```http
|
||||
POST /api/v1/users
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | superadmin, companyAdmin | Create new user |
|
||||
|
||||
### Update User
|
||||
```http
|
||||
PATCH /api/v1/users/{id}
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | Any authenticated | Update user profile |
|
||||
|
||||
### Delete User
|
||||
```http
|
||||
DELETE /api/v1/users/{id}
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | superadmin | Delete user |
|
||||
|
||||
### List User Roles
|
||||
```http
|
||||
GET /api/v1/users/roles
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | superadmin, admin | List available roles |
|
||||
|
||||
---
|
||||
|
||||
## 💼 Module: Jobs
|
||||
|
||||
### List Jobs (Public)
|
||||
```http
|
||||
GET /api/v1/jobs
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Public | ❌ | Guest | Search and filter jobs |
|
||||
|
||||
**Query Parameters:**
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `q` | string | Search query (title, description) |
|
||||
| `location` | string | Filter by location |
|
||||
| `type` | string | Employment type filter |
|
||||
| `workMode` | string | `onsite`, `hybrid`, `remote` |
|
||||
| `page` | int | Page number (default: 1) |
|
||||
| `limit` | int | Items per page (default: 20) |
|
||||
|
||||
### Get Job by ID
|
||||
```http
|
||||
GET /api/v1/jobs/{id}
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Public | ❌ | Guest | Get job details |
|
||||
|
||||
### Create Job
|
||||
```http
|
||||
POST /api/v1/jobs
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Public* | ❌ | companyAdmin, recruiter | Create new job posting |
|
||||
|
||||
### Update Job
|
||||
```http
|
||||
PUT /api/v1/jobs/{id}
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Public* | ❌ | companyAdmin, recruiter | Update job posting |
|
||||
|
||||
### Delete Job
|
||||
```http
|
||||
DELETE /api/v1/jobs/{id}
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Public* | ❌ | companyAdmin, recruiter | Delete job posting |
|
||||
|
||||
### List Jobs for Moderation
|
||||
```http
|
||||
GET /api/v1/jobs/moderation
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | superadmin, admin | List all jobs for moderation |
|
||||
|
||||
### Update Job Status
|
||||
```http
|
||||
PATCH /api/v1/jobs/{id}/status
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | superadmin, admin | Approve/reject/close job |
|
||||
|
||||
### Duplicate Job
|
||||
```http
|
||||
POST /api/v1/jobs/{id}/duplicate
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | superadmin, admin | Duplicate job posting |
|
||||
|
||||
---
|
||||
|
||||
## 📝 Module: Applications
|
||||
|
||||
### Create Application
|
||||
```http
|
||||
POST /api/v1/applications
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Public | ❌ | Guest/JobSeeker | Apply to a job |
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"job_id": 123,
|
||||
"name": "John Doe",
|
||||
"email": "john@example.com",
|
||||
"phone": "+55 11 99999-9999",
|
||||
"message": "I am interested in this position...",
|
||||
"resume_url": "https://..."
|
||||
}
|
||||
```
|
||||
|
||||
### List Applications
|
||||
```http
|
||||
GET /api/v1/applications
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Public | ❌ | companyAdmin, recruiter | List applications |
|
||||
|
||||
### Get Application by ID
|
||||
```http
|
||||
GET /api/v1/applications/{id}
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Public | ❌ | companyAdmin, recruiter | Get application details |
|
||||
|
||||
### Update Application Status
|
||||
```http
|
||||
PUT /api/v1/applications/{id}/status
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Public | ❌ | companyAdmin, recruiter | Update application status |
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"status": "hired",
|
||||
"notes": "Approved after final interview"
|
||||
}
|
||||
```
|
||||
|
||||
**Status Values:**
|
||||
- `pending` - Awaiting review
|
||||
- `reviewed` - Seen by recruiter
|
||||
- `shortlisted` - Selected for interview
|
||||
- `rejected` - Not selected
|
||||
- `hired` - Offer accepted
|
||||
|
||||
---
|
||||
|
||||
## 🔔 Module: Notifications
|
||||
|
||||
### List Notifications
|
||||
```http
|
||||
GET /api/v1/notifications
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | Any authenticated | Get user notifications |
|
||||
|
||||
---
|
||||
|
||||
## 🏷️ Module: Tags
|
||||
|
||||
### List Tags
|
||||
```http
|
||||
GET /api/v1/tags
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | superadmin, admin | List all tags |
|
||||
|
||||
### Create Tag
|
||||
```http
|
||||
POST /api/v1/tags
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | superadmin, admin | Create new tag |
|
||||
|
||||
### Update Tag
|
||||
```http
|
||||
PATCH /api/v1/tags/{id}
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | superadmin, admin | Update tag |
|
||||
|
||||
---
|
||||
|
||||
## 👤 Module: Candidates
|
||||
|
||||
### List Candidates
|
||||
```http
|
||||
GET /api/v1/candidates
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | superadmin, admin | List all candidates |
|
||||
|
||||
---
|
||||
|
||||
## 📊 Module: Audit
|
||||
|
||||
### List Login Audits
|
||||
```http
|
||||
GET /api/v1/audit/logins
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | superadmin, admin | Login audit trail |
|
||||
|
||||
---
|
||||
|
||||
## 📦 Module: Storage
|
||||
|
||||
### Generate Upload URL
|
||||
```http
|
||||
POST /api/v1/storage/upload-url
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | Any authenticated | Get S3 presigned upload URL |
|
||||
|
||||
### Generate Download URL
|
||||
```http
|
||||
POST /api/v1/storage/download-url
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | Any authenticated | Get S3 presigned download URL |
|
||||
|
||||
### Delete File
|
||||
```http
|
||||
DELETE /api/v1/storage/files
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Protected | ✅ | Any authenticated | Delete file from S3 |
|
||||
|
||||
---
|
||||
|
||||
## 📚 Module: Documentation
|
||||
|
||||
### Swagger UI
|
||||
```http
|
||||
GET /docs/
|
||||
```
|
||||
| Field | Auth | Roles | Description |
|
||||
|-------|------|-------|-------------|
|
||||
| Public | ❌ | Guest | Interactive API documentation |
|
||||
|
||||
---
|
||||
|
||||
## 🔑 Permission Matrix
|
||||
|
||||
| Route | Guest | JobSeeker | Recruiter | CompanyAdmin | Admin | SuperAdmin |
|
||||
|-------|-------|-----------|-----------|--------------|-------|------------|
|
||||
| `POST /auth/login` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `POST /auth/register` | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| `GET /jobs` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `GET /jobs/{id}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `POST /jobs` | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `POST /applications` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
| `GET /users/me` | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `GET /users` | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
|
||||
| `DELETE /users/{id}` | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
|
||||
| `GET /notifications` | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `GET /audit/logins` | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
|
||||
| `GET /jobs/moderation` | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 🆔 ID Formats
|
||||
|
||||
| Entity | ID Type | Example |
|
||||
|--------|---------|---------|
|
||||
| Users | INT (SERIAL) | `1`, `42`, `1337` |
|
||||
| Companies | INT (SERIAL) | `1`, `15`, `100` |
|
||||
| Jobs | INT (SERIAL) | `1`, `500`, `2500` |
|
||||
| Notifications | UUID v7 | `019438a1-2b3c-7abc-8123-4567890abcdef` |
|
||||
| Tickets | UUID v7 | `019438a2-3c4d-7xyz-9abc-def0123456789` |
|
||||
| Payments | UUID v7 | `019438a3-4e5f-7mno-pqrs-tuvwxyz012345` |
|
||||
|
||||
---
|
||||
|
||||
## 📝 Error Responses
|
||||
|
||||
### 400 Bad Request
|
||||
```json
|
||||
{
|
||||
"error": "Invalid request body",
|
||||
"details": "Field 'email' is required"
|
||||
}
|
||||
```
|
||||
|
||||
### 401 Unauthorized
|
||||
```json
|
||||
{
|
||||
"error": "Unauthorized",
|
||||
"message": "Invalid or expired token"
|
||||
}
|
||||
```
|
||||
|
||||
### 403 Forbidden
|
||||
```json
|
||||
{
|
||||
"error": "Forbidden",
|
||||
"message": "Insufficient permissions"
|
||||
}
|
||||
```
|
||||
|
||||
### 404 Not Found
|
||||
```json
|
||||
{
|
||||
"error": "Not Found",
|
||||
"message": "Resource not found"
|
||||
}
|
||||
```
|
||||
|
||||
### 500 Internal Server Error
|
||||
```json
|
||||
{
|
||||
"error": "Internal Server Error",
|
||||
"message": "An unexpected error occurred"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Related Documentation
|
||||
|
||||
- [Database Schema](DATABASE.md)
|
||||
- [Roadmap](ROADMAP.md)
|
||||
- [Tasks](TASKS.md)
|
||||
|
|
@ -192,15 +192,15 @@ export async function seedAcmeCorp() {
|
|||
for (let b = 0; b < 4; b++) {
|
||||
selectedBenefits.push(benefits[(i + b) % benefits.length]);
|
||||
}
|
||||
const jobId = crypto.randomUUID();
|
||||
|
||||
|
||||
await pool.query(`
|
||||
INSERT INTO jobs (id, company_id, created_by, title, description,
|
||||
INSERT INTO jobs ( company_id, created_by, title, description,
|
||||
salary_min, salary_max, salary_type, employment_type, working_hours,
|
||||
location, requirements, benefits, visa_support, language_level, status, work_mode)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
|
||||
`, [
|
||||
jobId,
|
||||
|
||||
acmeCompanyId,
|
||||
seedUserId,
|
||||
title,
|
||||
|
|
|
|||
|
|
@ -294,14 +294,14 @@ async function createCompanyAndJobs(companyData, jobs) {
|
|||
|
||||
for (let i = 0; i < jobs.length; i++) {
|
||||
const job = jobs[i];
|
||||
const jobId = crypto.randomUUID();
|
||||
|
||||
await pool.query(`
|
||||
INSERT INTO jobs (id, company_id, created_by, title, description,
|
||||
INSERT INTO jobs ( company_id, created_by, title, description,
|
||||
salary_min, salary_max, salary_type, employment_type, working_hours,
|
||||
location, requirements, benefits, visa_support, language_level, status, work_mode)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
|
||||
`, [
|
||||
jobId,
|
||||
|
||||
companyId,
|
||||
seedUserId,
|
||||
job.title,
|
||||
|
|
|
|||
|
|
@ -190,14 +190,14 @@ export async function seedStarkIndustries() {
|
|||
|
||||
for (let i = 0; i < starkJobs.length; i++) {
|
||||
const job = starkJobs[i];
|
||||
const jobId = crypto.randomUUID();
|
||||
|
||||
await pool.query(`
|
||||
INSERT INTO jobs (id, company_id, created_by, title, description,
|
||||
INSERT INTO jobs ( company_id, created_by, title, description,
|
||||
salary_min, salary_max, salary_type, employment_type, working_hours,
|
||||
location, requirements, benefits, visa_support, language_level, status, work_mode)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
|
||||
`, [
|
||||
jobId,
|
||||
|
||||
companyId,
|
||||
seedUserId,
|
||||
job.title,
|
||||
|
|
@ -275,14 +275,14 @@ export async function seedLosPollosHermanos() {
|
|||
|
||||
for (let i = 0; i < losPollosJobs.length; i++) {
|
||||
const job = losPollosJobs[i];
|
||||
const jobId = crypto.randomUUID();
|
||||
|
||||
await pool.query(`
|
||||
INSERT INTO jobs (id, company_id, created_by, title, description,
|
||||
INSERT INTO jobs ( company_id, created_by, title, description,
|
||||
salary_min, salary_max, salary_type, employment_type, working_hours,
|
||||
location, requirements, benefits, visa_support, language_level, status, work_mode)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
|
||||
`, [
|
||||
jobId,
|
||||
|
||||
companyId,
|
||||
seedUserId,
|
||||
job.title,
|
||||
|
|
@ -362,14 +362,14 @@ export async function seedSpringfieldNuclear() {
|
|||
|
||||
for (let i = 0; i < springfieldJobs.length; i++) {
|
||||
const job = springfieldJobs[i];
|
||||
const jobId = crypto.randomUUID();
|
||||
|
||||
await pool.query(`
|
||||
INSERT INTO jobs (id, company_id, created_by, title, description,
|
||||
INSERT INTO jobs ( company_id, created_by, title, description,
|
||||
salary_min, salary_max, salary_type, employment_type, working_hours,
|
||||
location, requirements, benefits, visa_support, language_level, status, work_mode)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
|
||||
`, [
|
||||
jobId,
|
||||
|
||||
companyId,
|
||||
seedUserId,
|
||||
job.title,
|
||||
|
|
|
|||
|
|
@ -97,15 +97,14 @@ export async function seedJobs() {
|
|||
const employmentType = employmentTypes[i % employmentTypes.length];
|
||||
const currency = currencies[i % currencies.length];
|
||||
const salaryType = salaryTypes[i % salaryTypes.length];
|
||||
const jobId = crypto.randomUUID();
|
||||
|
||||
// jobs.id is SERIAL - let DB auto-generate
|
||||
await pool.query(`
|
||||
INSERT INTO jobs (id, company_id, created_by, title, description,
|
||||
INSERT INTO jobs (company_id, created_by, title, description,
|
||||
salary_min, salary_max, salary_type, currency, employment_type, working_hours,
|
||||
location, requirements, benefits, visa_support, language_level, status, work_mode)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
|
||||
`, [
|
||||
jobId,
|
||||
company.id,
|
||||
seedUserId,
|
||||
title,
|
||||
|
|
|
|||
Loading…
Reference in a new issue