# GIIP Issue API -- Reference for the `giip-issue` Skill

This is a condensed, agent-facing reference for the same API documented in
full (with live-tested evidence, dates, and change history) at:

- Korean: `/ko/guides/giip-issue-api`
- Japanese: `/ja/guides/giip-issue-api`
- English: `/en/guides/giip-issue-api`

When this reference and the full guide disagree, the full guide is the
source of truth -- it is updated more frequently and carries dated,
live-verified evidence for every claim. This file exists so the skill does
not require a network fetch of the guide page just to know the shape of the
API.

## Base URL

```
https://giipfaw.azurewebsites.net/api
```

## Authentication -- `x-api-key` header only

- Send the key as the `x-api-key` HTTP header. `Authorization: Bearer <key>`
  also works as an alternate header name for the same value.
- **Not supported**: a `token` field in the JSON body, or a `?token=`
  query-string parameter. Both return `401 {"error":"Auth required"}` --
  the server literally never reads the key from those locations.
- Never put the key in a URL, log line, file, or exception message. Query
  strings in particular end up in server logs, browser history, and proxy
  logs even when the request itself succeeds.
- Three kinds of key work through the same header (project SK, per-user
  fixed key, login-session AK); the API cannot tell you which kind you used
  from the response. Their permission *scope* differs, but this skill
  treats them interchangeably -- it only ever sends whatever value is in
  `GIIP_API_KEY`.

## `401` has two distinct meanings

| Response | Meaning |
|---|---|
| `{"error":"Auth required"}` | The key never reached the server -- missing header, or an unsupported delivery method (body `token`, query `?token=`). |
| `{"error":"Invalid session"}` | The key reached the server but is not valid -- typo, a project SK that was rotated/deactivated (`SKStatus=0`), or an expired login-session AK (24h limit). |

## csn permission mismatch is *not* a 401

If the key is valid but has no access to the requested csn, the API does
**not** return 401. Instead:

| Call | Observed behavior |
|---|---|
| `GET /giipIssues?csn=<no access>` | `200 {"issues":[]}` -- an empty array, not an error. |
| `POST /giipIssues` with a `csn` you don't have access to | `200` "success", but the issue is silently created under the **key's own home csn** instead of the one you specified. |
| `GET`/`PUT`/comment on a specific `isn` you have no access to | `404 {"error":"Issue not found or no permission"}` |

This is why the safety contract below requires a read preflight before
every create, and a re-fetch after every create.

## Endpoints

### `GET /giipIssues?csn={csn}[&status={status}]` -- list

Returns `{"issues":[...]}`. Use this as the create preflight: if `issues`
comes back empty, permission on that csn is unconfirmed and no write should
be attempted.

### `GET /giipIssues?isn={isn}` -- get one

Returns `{"issues":[{...}]}` (a one-element array) or an empty array if the
isn does not exist or you lack permission on its csn.

### `POST /giipIssues` -- create

```json
{
  "title": "required",
  "content": "required",
  "status": "PENDING",
  "csn": 47,
  "target_lssn": null,
  "agent_workflow": null
}
```

Success: `{"isn":577,"message":"Issue created","success":true}`. Omit
`status` to default to `PENDING` server-side is not guaranteed by this
skill's client -- the skill always sends an explicit `status` (defaulting to
`PENDING` itself) so behavior is predictable regardless of server defaults.

**Always re-fetch by the returned `isn` afterward** (see csn clamp above)
and compare csn/title/status against what you asked for before reporting
success.

### `PUT /giipIssues` -- partial update

The underlying stored procedure (`pApiGiipIssuePutbyAK`) updates each field
with `ISNULL(@value, existing)` -- **any field you omit from the JSON body
is preserved**, but any field you include (including an empty string)
overwrites the stored value.

Minimal, safe status change:

```json
{"isn": 7890, "status": "DONE"}
```

Do **not** add `title`/`content`/`csn` to this body unless you explicitly
intend to change them.

**Do not use the `giipApiSk2` `GiipIssuePut` command for this.** That is a
different, legacy code path mapped to `pApiGiipIssuePutbySK`, which is a
**full overwrite** stored procedure (no `ISNULL` coalescing). Calling it
with only a status value will overwrite `title` and `content` with garbage
while still reporting a fake `RstVal:200` success. This skill never calls
`giipApiSk2` at all -- it only uses the dedicated REST endpoints above.

### `POST /giipIssueComments` -- add a comment

```json
{"isn": 7890, "content": "Comment body", "author": "your-label", "issuetype": "comment"}
```

Required: `isn`, `content`. If omitted, `author` and `issuetype` default to
values this skill's client sets itself (`giip-issue-skill` / `comment`).

**The stored `author` may not equal what you sent.** Depending on the
credential type and server-side identity resolution, the server can
normalize `author` to the caller's real account name. Never assume the
`author` value you sent was persisted verbatim -- re-fetch the comment if
you need to confirm what was actually stored.

### `GET /giipIssueComments?isn={isn}` -- list comments

Returns `{"comments":[...]}`.

### No delete endpoint

The GIIP Issue API does not expose a delete operation for issues or
comments. This skill has no `delete` subcommand, and none should be added
without a corresponding, verified server endpoint.

## Timeouts and retries

- Use a request timeout (this skill's client uses 15 seconds).
- On timeout or an unclear result from a write (POST/PUT), never
  auto-retry. Re-check with a `GET` (list or get-by-isn) to see whether the
  write actually landed before doing anything else.
