> ## Documentation Index
> Fetch the complete documentation index at: https://docs.golfjoin.co.kr/llms.txt
> Use this file to discover all available pages before exploring further.

# 티 매물

> 조인·양도 티를 등록하고, 수정하고, 마감합니다.

> 매니저 본인의 티 매물을 등록·조회·수정·마감합니다. 등록한 매물은 조인성공 검색 결과와 골프장 페이지에 바로 노출됩니다.

## 무엇을 하나요

| 경로 | 용도 |
| - | - |
| `POST /api/v1/manager/contents` | 티 매물 등록 |
| `GET /api/v1/manager/contents` | 내 매물 목록 |
| `GET /api/v1/manager/contents/{id}` | 매물 단건 조회 |
| `PATCH /api/v1/manager/contents` | 매물 수정, 또는 `action: "close"`로 마감 |
| `POST /api/v1/manager/bump` | 끌어올리기 |
| `GET /api/v1/manager/usage` | 이번 달 게시 횟수와 플랜 한도 |

삭제 API는 따로 없습니다. 모집이 끝났거나 내리고 싶은 매물은 **마감**하세요. 마감한 매물은 검색에서 빠지고 다시 열 수 없습니다.

## 이용 조건

* 조인성공에서 승인된 매니저 계정이어야 합니다.
* 플랜을 이용 중이어야 합니다. 플랜이 없으면 `403 no_plan`을 돌려줍니다.
* 등록과 끌어올리기는 플랜의 **월 게시 한도**를 1건씩 차감합니다. 수정과 마감은 차감하지 않습니다.

## 등록

### 요청

```bash theme={null}
curl -X POST https://golfjoin.co.kr/api/v1/manager/contents \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "golfCourseId": 3,
    "courseLabel": "레이크",
    "contentType": "JOIN",
    "playDate": "2026-12-20",
    "teeTime": "07:12",
    "playerCount": 1,
    "joinCondition": "무관",
    "price": 150000,
    "deposit": 0,
    "caddyType": "일반캐디",
    "holeCount": 18,
    "cartIncluded": true,
    "mealIncluded": false,
    "allowCall": true,
    "contentText": "카트비 포함, 캐디피 별도입니다.",
    "externalId": "ext-20261220-0001"
  }'
```

| 필드 | 필수 | 형식 | 설명 |
| - | - | - | - |
| `golfCourseId` | O | integer | 골프장 ID. [골프장](https://docs.golfjoin.co.kr/api-introduction/golf-courses.md)에서 찾습니다. |
| `contentType` | O | string | `JOIN`(조인) 또는 `TRANSFER`(양도) |
| `playDate` | O | string | 플레이 날짜 `YYYY-MM-DD` (KST) |
| `teeTime` | O | string | 티업 시간 `HH:MM`, 24시간제 (KST) |
| `courseLabel` | X | string 또는 null | 코스 이름, 30자 이내. 골프장 목록의 `fields[].name`을 쓰세요. |
| `playerCount` | X | integer 또는 null | 조인은 모집 인원, 양도는 팀 인원. 1\~4 |
| `joinCondition` | X | string 또는 null | 조인 조건. 아래 값 중 하나. 양도는 `null` |
| `price` | X | integer 또는 null | 1인 그린피, **원 단위** |
| `deposit` | X | integer 또는 null | 선입금, 원 단위. `0`은 선입금 없음 |
| `caddyType` | X | string 또는 null | 캐디 조건. 아래 값 중 하나 |
| `holeCount` | X | integer 또는 null | `9`, `18`, `27`, `36` |
| `cartIncluded` | X | boolean 또는 null | 카트비 포함 여부 |
| `mealIncluded` | X | boolean 또는 null | 식사 포함 여부 |
| `allowCall` | X | boolean | 전화 문의 허용. 기본 `true`. `false`면 문자 문의만 받습니다. |
| `contentText` | X | string 또는 null | 매물 안내문(메모). 위약 규정, 포함 사항 등을 적습니다. |
| `title` | X | string 또는 null | 제목, 100자 이내. 비우면 `골프장명 날짜 시간`으로 자동 생성됩니다. |
| `externalId` | X | string | 연동 프로그램 쪽 고유 번호. 영문·숫자와 `_ . : -`, 1\~100자 |

`courseLabel`과 `title`에는 `<`, `>`를 넣을 수 없습니다.

#### 값 목록

| 필드 | 값 |
| - | - |
| `joinCondition` | `무관`, `부부커플`, `남자`, `여자` |
| `caddyType` | `일반캐디`, `노캐디`, `운전캐디`, `새싹캐디` |

목록에 없는 값을 보내도 저장은 되지만, 조인성공의 조건 필터에 걸리지 않습니다. 목록 안의 값을 쓰세요. 이 목록에 없는 조건(예: 남1·여1 모집)은 `contentText`에 적습니다.

### 응답

```json theme={null}
{
  "id": 188201,
  "usage": {
    "plan": { "id": 1, "name": "베이직", "monthlyPostLimit": 999999 },
    "posts": { "used": 12, "limit": 999999 },
    "month": "2026-11"
  }
}
```

| 필드 | 형식 | 설명 |
| - | - | - |
| `id` | integer | 조인성공 매물 번호. 이후 수정·마감에 씁니다. |
| `usage` | object | 등록 직후의 월 사용량. [월 사용량](#월-사용량)과 같은 형식입니다. |

### 중복 등록 막기

`externalId`를 함께 보내면, 같은 매니저가 같은 `externalId`로 다시 등록할 때 새 매물을 만들지 않고 기존 매물 번호를 돌려줍니다. 이때는 월 한도를 차감하지 않습니다.

```json theme={null}
{ "id": 188201, "duplicate": true }
```

네트워크 오류로 등록 응답을 못 받았을 때 같은 요청을 그대로 다시 보내도 안전합니다. 마감한 매물의 `externalId`도 계속 기존 매물로 연결되니, 같은 티를 다시 올릴 때는 새 `externalId`를 쓰세요.

> [!NOTE]
>
> `externalId`는 매니저 계정마다 따로 관리합니다. 다른 매니저가 같은 값을 써도 서로 영향이 없습니다. 등록 뒤에는 바꿀 수 없습니다.

## 수정

바꿀 필드만 보냅니다. 보내지 않은 필드는 그대로 둡니다. 값을 지우려면 그 필드를 `null`로 보내세요.

```bash theme={null}
curl -X PATCH https://golfjoin.co.kr/api/v1/manager/contents \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{ "id": 188201, "price": 140000, "deposit": null }'
```

```json theme={null}
{ "id": 188201, "updated": true }
```

| 필드 | 필수 | 설명 |
| - | - | - |
| `id` | O | 매물 번호 |
| 그 밖의 등록 필드 | X | 등록과 같은 형식. `externalId`는 바꿀 수 없어 무시합니다. |

* 실제로 바뀐 값이 있으면 저장하고, 매물이 목록 위쪽으로 올라갑니다(최신 갱신순). 이때는 월 한도를 차감하지 않습니다.
* 보낸 값이 지금 값과 모두 같으면 아무것도 바꾸지 않고 `{ "id": ..., "updated": false }`를 돌려줍니다. 목록 순서도 바뀌지 않습니다.
* 골프장·날짜·시간을 바꾸면 자동 제목도 새 값으로 바뀝니다. `title`을 직접 보냈다면 그 값을 씁니다.

## 마감

```bash theme={null}
curl -X PATCH https://golfjoin.co.kr/api/v1/manager/contents \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{ "id": 188201, "action": "close" }'
```

```json theme={null}
{ "ok": true, "status": "EXPIRED" }
```

조인이 다 찼거나, 양도가 끝났거나, 티업 시간이 지난 매물은 마감해 주세요. 이미 마감한 매물을 다시 마감해도 같은 응답을 돌려줍니다.

## 끌어올리기

매물을 목록 맨 위(최신 갱신순)로 올립니다. 내용은 바꾸지 않습니다. 매물 1건마다 월 게시 한도를 1건 차감합니다.

### 요청

매물을 골라서 올릴 때:

```bash theme={null}
curl -X POST https://golfjoin.co.kr/api/v1/manager/bump \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{ "contentIds": [188201, 188202] }'
```

오래된 매물을 한꺼번에 올릴 때:

```bash theme={null}
curl -X POST https://golfjoin.co.kr/api/v1/manager/bump \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{ "all": true, "olderThanDays": 3 }'
```

| 필드 | 필수 | 설명 |
| - | - | - |
| `contentIds` | △ | 올릴 매물 번호 목록. `all`을 쓰지 않으면 필수입니다. |
| `all` | △ | `true`면 노출 중인 내 매물 전체가 대상입니다. |
| `olderThanDays` | X | `all`과 함께 씁니다. 마지막 갱신 후 이 일수가 지난 매물만 올립니다. |

> [!WARNING]
>
> `all: true`만 보내고 `olderThanDays`를 빼면 노출 중인 매물 **전부**를 올리고, 그만큼 한도를 차감합니다. 보통은 `olderThanDays`를 함께 보내세요.

아래 매물은 대상에서 빠집니다.

* 마감했거나 노출 중이 아닌 매물
* 티업 시간이 이미 지난 매물(한국 시간 기준)
* 내 매물이 아닌 번호

남은 한도보다 대상이 많으면 **마지막 갱신이 오래된 매물부터** 한도만큼만 올리고 나머지는 건너뜁니다.

### 응답

```json theme={null}
{
  "bumped": 2,
  "skipped": 0,
  "requested": 2,
  "usage": {
    "plan": { "id": 1, "name": "베이직", "monthlyPostLimit": 999999 },
    "posts": { "used": 14, "limit": 999999 },
    "month": "2026-11"
  }
}
```

| 필드 | 형식 | 설명 |
| - | - | - |
| `requested` | integer | 조건에 맞아 올릴 대상이 된 매물 수 |
| `bumped` | integer | 실제로 올린 매물 수 |
| `skipped` | integer | 한도가 모자라 건너뛴 매물 수 |
| `usage` | object | 끌어올린 뒤의 월 사용량 |

대상이 하나도 없으면 `requested`, `bumped`, `skipped`가 모두 `0`입니다. 다른 사이트에서 수집한 매물(`crawled: true`)을 끌어올리면 그 매니저의 직접 관리로 바뀝니다(`locked: true`).

## 내 매물 목록

```bash theme={null}
curl https://golfjoin.co.kr/api/v1/manager/contents \
  -H "Authorization: Bearer {access_token}"
```

```json theme={null}
{
  "contents": [
    {
      "id": 188201,
      "contentType": "JOIN",
      "title": "라싸 2026-12-20 07:12",
      "courseLabel": "레이크",
      "price": 140000,
      "deposit": null,
      "playerCount": 1,
      "joinCondition": "무관",
      "caddyType": "일반캐디",
      "holeCount": 18,
      "status": "ACTIVE",
      "sourceUpdatedAt": "2026-11-02T08:10:31.000Z",
      "createdAt": "2026-11-02T08:01:12.000Z",
      "golfCourse": { "id": 3, "name": "라싸", "region": "GYEONGGI_NORTH" },
      "teeTime": { "playDate": "2026-12-20T00:00:00.000Z", "teeTime": "07:12" },
      "externalId": "ext-20261220-0001",
      "crawled": false,
      "locked": false
    }
  ]
}
```

| 필드 | 형식 | 설명 |
| - | - | - |
| `id` | integer | 매물 번호 |
| `status` | string | [상태값](#상태값) 참고 |
| `sourceUpdatedAt` | string | 마지막 갱신 시각(UTC). 목록은 이 값의 최신순입니다. |
| `teeTime.playDate` | string | 플레이 날짜. 앞 10자리(`YYYY-MM-DD`)만 쓰세요. |
| `teeTime.teeTime` | string | 티업 시간 `HH:MM` (KST) |
| `externalId` | string 또는 null | 등록 때 보낸 값. 없으면 `null` |
| `crawled` | boolean | `true`면 조인성공이 다른 사이트에서 수집한 매물입니다. 아래 설명 참고 |
| `locked` | boolean | 수집한 매물을 매니저가 수정·마감해 직접 관리로 바뀐 경우 `true` |

목록에는 매니저가 직접 올린 매물 전체(마감 포함)가 들어갑니다. 그 밖에 **조인성공이 다른 사이트에서 그 매니저 전화번호로 수집한 매물** 중 지금 노출 중인 것도 함께 들어갑니다(`crawled: true`). 수집한 매물을 수정하거나 마감하면 그 매니저의 직접 관리로 바뀌고(`locked: true`), 이후에는 원래 사이트의 변경이 반영되지 않습니다.

> [!NOTE]
>
> 목록은 페이지를 나누지 않고 한 번에 돌려줍니다. 1분에 한 번 이하로 조회하세요. 수정 화면처럼 매물 하나의 전체 항목이 필요하면 단건 조회를 쓰세요. 목록에는 `cartIncluded`, `mealIncluded`, `allowCall`, `contentText`가 없습니다.

## 단건 조회

```bash theme={null}
curl https://golfjoin.co.kr/api/v1/manager/contents/188201 \
  -H "Authorization: Bearer {access_token}"
```

```json theme={null}
{
  "content": {
    "id": 188201,
    "contentType": "JOIN",
    "price": 140000,
    "deposit": null,
    "playerCount": 1,
    "joinCondition": "무관",
    "caddyType": "일반캐디",
    "holeCount": 18,
    "courseLabel": "레이크",
    "cartIncluded": true,
    "mealIncluded": false,
    "allowCall": true,
    "contentText": "카트비 포함, 캐디피 별도입니다.",
    "status": "ACTIVE",
    "golfCourse": { "id": 3, "name": "라싸", "region": "GYEONGGI_NORTH", "address": "경기도 포천시 이동면 제비울2길 161", "roadAddress": null },
    "teeTime": { "playDate": "2026-12-20T00:00:00.000Z", "teeTime": "07:12" },
    "externalId": "ext-20261220-0001"
  }
}
```

내 매물이 아니면 `404 not_found`를 돌려줍니다.

## 상태값

| `status` | 의미 |
| - | - |
| `ACTIVE` | 노출 중 |
| `EXPIRED` | 마감 |
| `STALE`, `DISCOVERED` | 수집한 매물의 노출 중 상태. 노출 중으로 보면 됩니다. |
| `PENDING` | 수집한 매물이 노출 순서를 기다리는 중 |

매니저가 직접 올린 매물은 `ACTIVE` 또는 `EXPIRED`만 됩니다.

## 월 사용량

```bash theme={null}
curl https://golfjoin.co.kr/api/v1/manager/usage \
  -H "Authorization: Bearer {access_token}"
```

```json theme={null}
{
  "usage": {
    "plan": { "id": 1, "name": "베이직", "monthlyPostLimit": 999999 },
    "posts": { "used": 12, "limit": 999999 },
    "month": "2026-11"
  }
}
```

| 필드 | 형식 | 설명 |
| - | - | - |
| `plan` | object 또는 null | 이용 중인 플랜. 없으면 `null` |
| `posts.used` | integer | 이번 달 게시 횟수(등록 + 끌어올리기) |
| `posts.limit` | integer | 월 한도. `-1`은 무제한 |
| `month` | string | 집계 월 `YYYY-MM` |

한도를 다 쓰면 등록이 `429 post_quota_exceeded`로 실패합니다. 수정과 마감은 계속 할 수 있습니다.

## 오류

| HTTP | `error` | 언제 |
| - | - | - |
| 400 | `invalid_input` | 필수 필드가 없거나 형식이 맞지 않습니다. |
| 403 | `forbidden` | 토큰 문제, 권한 범위 밖, 매니저 승인 해제 |
| 400 | `no_target` | 끌어올리기에서 `contentIds`가 비었고 `all`도 없습니다. |
| 403 | `no_plan` | 플랜을 이용 중이 아닙니다. |
| 404 | `course_not_found` | 없는 골프장 ID입니다. 골프장이 정리(병합)된 경우일 수 있습니다. |
| 404 | `not_found` | 없는 매물이거나 내 매물이 아닙니다. |
| 429 | `post_quota_exceeded` | 이번 달 게시 한도를 다 썼습니다. 응답에 `usage`가 함께 옵니다. |

## 관련 문서

* [인증](https://docs.golfjoin.co.kr/api-introduction/authentication.md)
* [골프장](https://docs.golfjoin.co.kr/api-introduction/golf-courses.md)
* [에러 코드](https://docs.golfjoin.co.kr/api-introduction/error-codes.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.