{
  "info": {
    "_postman_id": "319ade8f-14ae-4ff2-94d5-bdf8d2154f6d",
    "name": "Lit Circle Bookstore Events API",
    "description": "Test Lit Circle's bookstore-scoped Events API with a dedicated, revocable credential. Import the companion environment template, add the credential only to your local environment, and keep allowWrites set to false until you intentionally want to create, replace, or delete production Event data. Never export or share an environment after adding a credential.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{apiKey}}",
        "type": "string"
      }
    ]
  },
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "if (!pm.environment.get('apiKey')) {",
          "  throw new Error('Set apiKey in the active Lit Circle environment before sending requests.');",
          "}"
        ]
      }
    }
  ],
  "item": [
    {
      "name": "Taxonomy",
      "description": "Read the current labels accepted by Event Source writes before constructing a payload.",
      "item": [
        {
          "name": "List accepted Event Types",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/event-types",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "event-types"]
            },
            "description": "Returns the Event Type labels currently accepted by POST and PUT."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Returns accepted Event Types', function () {",
                  "  pm.response.to.have.status(200);",
                  "  pm.expect(pm.response.json().data).to.be.an('array');",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "List accepted Genres",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/genres",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "genres"]
            },
            "description": "Returns the Genre labels currently accepted by POST and PUT."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Returns accepted Genres', function () {",
                  "  pm.response.to.have.status(200);",
                  "  pm.expect(pm.response.json().data).to.be.an('array');",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "List available Neighborhoods",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/neighborhoods",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "neighborhoods"]
            },
            "description": "Returns Neighborhood IDs and names available in the credential's bookstore Area."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Returns available Neighborhoods', function () {",
                  "  pm.response.to.have.status(200);",
                  "  pm.expect(pm.response.json().data).to.be.an('array');",
                  "});"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "Read Events",
      "description": "Read the complete bookstore-associated Event snapshot or one associated Public Event.",
      "item": [
        {
          "name": "List bookstore Events",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/events",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "events"]
            },
            "description": "Returns the authoritative default Event Feed Snapshot. Save its ETag for conditional polling."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Returns an Event Feed Snapshot', function () {",
                  "  pm.response.to.have.status(200);",
                  "  pm.expect(pm.response.json().data).to.be.an('array');",
                  "});",
                  "const etag = pm.response.headers.get('ETag');",
                  "if (etag) pm.collectionVariables.set('etag', etag);"
                ]
              }
            }
          ]
        },
        {
          "name": "Poll bookstore Events conditionally",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "If-None-Match",
                "value": "{{etag}}",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/events",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "events"]
            },
            "description": "Reuses the ETag captured by List bookstore Events. An unchanged snapshot returns 304."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Returns a snapshot or Not Modified', function () {",
                  "  pm.expect(pm.response.code).to.be.oneOf([200, 304]);",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Get one associated Event",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/events/{{eventId}}",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "events", "{{eventId}}"]
            },
            "description": "Returns one associated Public Event. Set eventId manually or run the create request first."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Returns the requested Event', function () {",
                  "  pm.response.to.have.status(200);",
                  "  pm.expect(pm.response.json().data.id).to.eql(pm.collectionVariables.get('eventId'));",
                  "});"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "Write Events",
      "description": "These requests write production Event data. They are blocked until allowWrites is explicitly set to true in collection variables.",
      "event": [
        {
          "listen": "prerequest",
          "script": {
            "type": "text/javascript",
            "exec": [
              "if (pm.collectionVariables.get('allowWrites') !== 'true') {",
              "  throw new Error('Production writes are disabled. Set collection variable allowWrites to true only when you intend to modify Event data.');",
              "}"
            ]
          }
        }
      ],
      "item": [
        {
          "name": "Create a source-managed Event",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"externalEventId\": \"{{externalEventId}}\",\n  \"sourceUpdatedAt\": \"{{sourceUpdatedAt}}\",\n  \"sourceState\": \"scheduled\",\n  \"sourceUrl\": \"https://bookstore.example/events/{{externalEventId}}\",\n  \"title\": \"Postman API Test Event\",\n  \"description\": {\n    \"markdown\": \"Created intentionally from the Lit Circle Postman collection.\"\n  },\n  \"schedule\": {\n    \"startsAt\": \"{{startsAt}}\",\n    \"endsAt\": \"{{endsAt}}\"\n  },\n  \"eventTypes\": [\"Author Event\"],\n  \"genres\": [],\n  \"ageRating\": \"allAges\",\n  \"venue\": {\n    \"kind\": \"bookstore\"\n  },\n  \"admission\": {\n    \"isFree\": true,\n    \"priceText\": null,\n    \"ticketIncludesBook\": false,\n    \"registration\": {\n      \"required\": false,\n      \"url\": null\n    },\n    \"ticketUrl\": null\n  },\n  \"image\": null\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/events",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "events"]
            },
            "description": "Creates one source-managed Event. The pre-request script generates a stable External Event ID and timestamp for safe identical retries. Clear externalEventId and sourceUpdatedAt before intentionally creating another Event."
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (!pm.collectionVariables.get('externalEventId')) {",
                  "  pm.collectionVariables.set('externalEventId', 'postman-' + Date.now());",
                  "}",
                  "if (!pm.collectionVariables.get('sourceUpdatedAt')) {",
                  "  pm.collectionVariables.set('sourceUpdatedAt', new Date().toISOString());",
                  "}",
                  "if (!pm.collectionVariables.get('startsAt')) {",
                  "  const startsAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);",
                  "  pm.collectionVariables.set('startsAt', startsAt.toISOString());",
                  "  pm.collectionVariables.set('endsAt', new Date(startsAt.getTime() + 90 * 60 * 1000).toISOString());",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Creates or idempotently replays the Event', function () {",
                  "  pm.expect(pm.response.code).to.be.oneOf([200, 201]);",
                  "});",
                  "const payload = pm.response.json();",
                  "if (payload.data && payload.data.id) pm.collectionVariables.set('eventId', payload.data.id);"
                ]
              }
            }
          ]
        },
        {
          "name": "Replace a source-managed Event",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"externalEventId\": \"{{externalEventId}}\",\n  \"sourceUpdatedAt\": \"{{replacementSourceUpdatedAt}}\",\n  \"sourceState\": \"scheduled\",\n  \"sourceUrl\": \"https://bookstore.example/events/{{externalEventId}}\",\n  \"title\": \"Postman API Test Event — Updated\",\n  \"description\": {\n    \"markdown\": \"Replaced intentionally from the Lit Circle Postman collection.\"\n  },\n  \"schedule\": {\n    \"startsAt\": \"{{startsAt}}\",\n    \"endsAt\": \"{{endsAt}}\"\n  },\n  \"eventTypes\": [\"Author Event\"],\n  \"genres\": [],\n  \"ageRating\": \"allAges\",\n  \"venue\": {\n    \"kind\": \"bookstore\"\n  },\n  \"admission\": {\n    \"isFree\": true,\n    \"priceText\": null,\n    \"ticketIncludesBook\": false,\n    \"registration\": {\n      \"required\": false,\n      \"url\": null\n    },\n    \"ticketUrl\": null\n  },\n  \"image\": null\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/events/{{eventId}}",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "events", "{{eventId}}"]
            },
            "description": "Replaces the source-managed field set for the Event captured by the create request. Clear replacementSourceUpdatedAt before preparing a newer replacement."
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (!pm.collectionVariables.get('eventId') || !pm.collectionVariables.get('externalEventId')) {",
                  "  throw new Error('Run Create a source-managed Event first, or set eventId and externalEventId manually.');",
                  "}",
                  "if (!pm.collectionVariables.get('replacementSourceUpdatedAt')) {",
                  "  pm.collectionVariables.set('replacementSourceUpdatedAt', new Date().toISOString());",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Replaces the source-managed Event', function () {",
                  "  pm.response.to.have.status(200);",
                  "  pm.expect(pm.response.json().data.id).to.eql(pm.collectionVariables.get('eventId'));",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Delete an API-created Event",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/events/{{eventId}}",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "events", "{{eventId}}"]
            },
            "description": "Permanently deletes the Event captured by the create request. Only API-created Events belonging to this credential's bookstore are eligible."
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (!pm.collectionVariables.get('eventId')) {",
                  "  throw new Error('Run Create a source-managed Event first, or set eventId manually.');",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Deletes the API-created Event', function () {",
                  "  pm.response.to.have.status(204);",
                  "});"
                ]
              }
            }
          ]
        }
      ]
    }
  ],
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://litcircle.org",
      "type": "string"
    },
    {
      "key": "allowWrites",
      "value": "false",
      "type": "string"
    },
    {
      "key": "eventId",
      "value": "",
      "type": "string"
    },
    {
      "key": "externalEventId",
      "value": "",
      "type": "string"
    },
    {
      "key": "sourceUpdatedAt",
      "value": "",
      "type": "string"
    },
    {
      "key": "replacementSourceUpdatedAt",
      "value": "",
      "type": "string"
    },
    {
      "key": "startsAt",
      "value": "",
      "type": "string"
    },
    {
      "key": "endsAt",
      "value": "",
      "type": "string"
    },
    {
      "key": "etag",
      "value": "",
      "type": "string"
    }
  ]
}
