{
  "info": {
    "_postman_id": "halvway-public-v1",
    "name": "Halvway REST API v1.0.0",
    "description": "Halvway finds the fairest, cheapest city for two friends to meet in the middle.\nThe web app and native clients share this API; row-level security applies as the\nauthenticated user and is never bypassed.\n\nAuth: obtain a session from `/auth/login` (or the Supabase SDK) and send\n`Authorization: Bearer <accessToken>` on endpoints marked with `bearerAuth`.\nAll endpoints are CORS-enabled and answer `OPTIONS` preflights.\n\n\nGenerated from public/openapi.yaml — run `bun run api:postman` to refresh.\n\nQuick start: select the \"Halvway\" environment, set `email`/`password`, send **Auth → Log in**. The token is stored in `accessToken` automatically and every secured request inherits it.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{accessToken}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://halvway.lovable.app/api/public/v1",
      "type": "string"
    },
    {
      "key": "commentId",
      "value": "",
      "type": "string"
    },
    {
      "key": "resourceId",
      "value": "",
      "type": "string"
    },
    {
      "key": "shareToken",
      "value": "",
      "type": "string"
    }
  ],
  "item": [
    {
      "name": "Meta",
      "item": [
        {
          "name": "Machine-readable endpoint index",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/",
              "host": [
                "{{baseUrl}}"
              ],
              "path": []
            },
            "description": "Public endpoint — no token needed.",
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "Full curated city catalogue",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/cities",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "cities"
              ]
            },
            "description": "Public endpoint — no token needed.",
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Auth",
      "item": [
        {
          "name": "Create an account",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auth/signup",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auth",
                "signup"
              ]
            },
            "description": "Public endpoint — no token needed.",
            "auth": {
              "type": "noauth"
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\",\n  \"displayName\": \"\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "const session = body.session || body.data?.session;",
                  "if (session?.accessToken) {",
                  "  pm.environment.set('accessToken', session.accessToken);",
                  "  pm.environment.set('refreshToken', session.refreshToken);",
                  "  console.log('Saved session for', session.user?.email || session.user?.id);",
                  "} else {",
                  "  console.warn('No session in response — check credentials or email confirmation.');",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Sign in with email and password",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auth/login",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auth",
                "login"
              ]
            },
            "description": "Public endpoint — no token needed.",
            "auth": {
              "type": "noauth"
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "const session = body.session || body.data?.session;",
                  "if (session?.accessToken) {",
                  "  pm.environment.set('accessToken', session.accessToken);",
                  "  pm.environment.set('refreshToken', session.refreshToken);",
                  "  console.log('Saved session for', session.user?.email || session.user?.id);",
                  "} else {",
                  "  console.warn('No session in response — check credentials or email confirmation.');",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Exchange a refresh token for a new session",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auth/refresh",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auth",
                "refresh"
              ]
            },
            "description": "Public endpoint — no token needed.",
            "auth": {
              "type": "noauth"
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"refreshToken\": \"{{refreshToken}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "const session = body.session || body.data?.session;",
                  "if (session?.accessToken) {",
                  "  pm.environment.set('accessToken', session.accessToken);",
                  "  pm.environment.set('refreshToken', session.refreshToken);",
                  "  console.log('Saved session for', session.user?.email || session.user?.id);",
                  "} else {",
                  "  console.warn('No session in response — check credentials or email confirmation.');",
                  "}"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "Profile",
      "item": [
        {
          "name": "Current profile",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/me",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "me"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`."
          },
          "response": []
        },
        {
          "name": "Update profile fields",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/me",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "me"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"display_name\": \"\",\n  \"bio\": \"\",\n  \"interests\": [\n    \"\"\n  ],\n  \"home_city_id\": \"\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Set the profile photo URL",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/me/avatar",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "me",
                "avatar"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"url\": \"\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Clear the profile photo",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/me/avatar",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "me",
                "avatar"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`."
          },
          "response": []
        },
        {
          "name": "Recompute the verification badge",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/me/verify",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "me",
                "verify"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`."
          },
          "response": []
        },
        {
          "name": "Live-fare availability",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/me/pricing",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "me",
                "pricing"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`."
          },
          "response": []
        },
        {
          "name": "Toggle live fares for this account",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/me/pricing",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "me",
                "pricing"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"liveFares\": true\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "People",
      "item": [
        {
          "name": "Search people by name",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/people?query=ana",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "people"
              ],
              "query": [
                {
                  "key": "query",
                  "value": "ana",
                  "disabled": false
                }
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Friends",
      "item": [
        {
          "name": "List friendships and pending requests",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/friends",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "friends"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`."
          },
          "response": []
        },
        {
          "name": "Send a friend request",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/friends",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "friends"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"personId\": \"\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "One connection plus the trips you share",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/friends/{{resourceId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "friends",
                "{{resourceId}}"
              ]
            },
            "description": "Returns the friendship (status, direction, the other person's profile) and\nevery meetup between the two of you. `id` is the friendship id.\n\n\nRequires `Authorization: Bearer {{accessToken}}`."
          },
          "response": []
        },
        {
          "name": "Accept or decline an incoming request",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/friends/{{resourceId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "friends",
                "{{resourceId}}"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"accept\": true\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Remove a friendship",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/friends/{{resourceId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "friends",
                "{{resourceId}}"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Invites",
      "item": [
        {
          "name": "List email invites you sent",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/invites",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "invites"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`."
          },
          "response": []
        },
        {
          "name": "Create an email invite",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/invites",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "invites"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Revoke an invite",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/invites/{{resourceId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "invites",
                "{{resourceId}}"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`."
          },
          "response": []
        },
        {
          "name": "Accept an invite token",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/invites/accept",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "invites",
                "accept"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"token\": \"\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Planning",
      "item": [
        {
          "name": "Rank in-between cities for you and a friend",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/meetpoints/search",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "meetpoints",
                "search"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"friendId\": \"\",\n  \"departDate\": \"2026-09-11\",\n  \"returnDate\": \"2026-09-11\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Meetups",
      "item": [
        {
          "name": "List your trips",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/meetups",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "meetups"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`."
          },
          "response": []
        },
        {
          "name": "Propose a trip",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/meetups",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "meetups"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"friendId\": \"\",\n  \"cityId\": \"\",\n  \"departDate\": \"2026-09-11\",\n  \"returnDate\": \"2026-09-11\",\n  \"yourPrice\": 0,\n  \"friendPrice\": 0,\n  \"currency\": \"EUR\",\n  \"priceSource\": \"estimate\",\n  \"fairness\": 0,\n  \"note\": \"\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Update trip status",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/meetups/{{resourceId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "meetups",
                "{{resourceId}}"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"status\": \"accepted\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Sharing",
      "item": [
        {
          "name": "Create (or fetch) a view-only share token",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/meetups/{{resourceId}}/share",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "meetups",
                "{{resourceId}}",
                "share"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`."
          },
          "response": []
        },
        {
          "name": "Revoke the share token",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/meetups/{{resourceId}}/share",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "meetups",
                "{{resourceId}}",
                "share"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`."
          },
          "response": []
        },
        {
          "name": "View a shared trip (no auth, view-only)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/shared-trips/{{shareToken}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "shared-trips",
                "{{shareToken}}"
              ]
            },
            "description": "Public endpoint — no token needed.",
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Comments",
      "item": [
        {
          "name": "Thread of messages on a trip (oldest first)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/meetups/{{resourceId}}/comments",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "meetups",
                "{{resourceId}}",
                "comments"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`."
          },
          "response": []
        },
        {
          "name": "Post a message on a trip",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/meetups/{{resourceId}}/comments",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "meetups",
                "{{resourceId}}",
                "comments"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"body\": \"\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Delete your own comment",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/meetups/{{resourceId}}/comments/{{commentId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "meetups",
                "{{resourceId}}",
                "comments",
                "{{commentId}}"
              ]
            },
            "description": "Requires `Authorization: Bearer {{accessToken}}`."
          },
          "response": []
        }
      ]
    }
  ]
}
