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

# Post a story

> Publish one uploaded image or one existing Vault image with the V2 Access API

`POST /v2/access/stories` accepts exactly one image. Set `mediaItems` to a numeric Vault image ID, a numeric string, or a completed `upload:` reference.

```json theme={null}
{
  "mediaItems": [123456789]
}
```

Authenticate with `apikey` and `x-connection-id` as described in [Access API](/api-reference/access/overview). The connection needs `stories:write`; uploading a new image also needs `vault:write`.

The optional `releaseForms` object accepts numeric user IDs in `users`, `partners`, and `guests` arrays. Other fields are rejected, including canvas dimensions, questions, and editor transformations. Apply image edits before uploading. Videos and GIFs are not supported by story creation.

## Upload an image and create the story

This Node.js example handles single-part and multipart PNG uploads. For another supported image format, change `contentType` to match the file.

```javascript theme={null}
import { readFile } from "node:fs/promises"
import { basename } from "node:path"

const api = "https://api.ofauth.com"
const filename = process.argv[2]
const bytes = await readFile(filename)
const contentType = "image/png"
const headers = {
  apikey: process.env.OFAUTH_API_KEY,
  "x-connection-id": process.env.CONNECTION_ID
}

async function post(path, body) {
  const response = await fetch(`${api}${path}`, {
    method: "POST",
    headers: { ...headers, "content-type": "application/json" },
    body: JSON.stringify(body)
  })
  if (!response.ok) throw new Error(await response.text())
  return response.json()
}

const upload = await post("/v2/access/uploads", {
  filename: basename(filename),
  size: bytes.length,
  contentType
})

for (const part of upload.parts) {
  const response = await fetch(part.uploadUrl, {
    method: "PUT",
    headers: { ...headers, "content-type": contentType },
    body: bytes.subarray(part.byteRange.start, part.byteRange.end + 1)
  })
  if (!response.ok) throw new Error(await response.text())
}

if (upload.uploadType === "multipart") {
  await post(`/v2/access/uploads/${encodeURIComponent(upload.mediaUploadId)}/finalize`, {})
}

const story = await post("/v2/access/stories", {
  mediaItems: [upload.mediaUploadId]
})
console.log(story.id)
```

Byte-range ends are inclusive. V2 single-part PUTs finalize the upload automatically; multipart uploads require finalization after every part has been uploaded. OFAuth calculates the part count from the file size.

A temporary upload reference is consumed when story creation succeeds. Start a new upload session for another story, or reuse a Vault image ID. Upload sessions started before this release must be restarted to record the detected file type and storage location.


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