Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: 'DrinkedIn API: Night Out, and bar data Places API'
description: '**Deprecation (7.2.1, SWE-1600):** the agent bar is retired. Its operations (agent registration, venues, drink orders, conversations, photos, referrals, vouchers, gift certificates, promotions, happy hours, the premium agent tier, wallets and withdrawals, focus groups, sponsorships and the agent-analytics feed) are marked `deprecated` from 2026-11-01; the matching MCP tools are removed on 2026-12-01. Night Out (https://ai.drinkedin.net/mcp/night-out), place search, the bars and cocktails data feeds and their licences stay.
API for DrinkedIn''s licensed bar data, cocktail recipes (the recipe text is a legacy import of unestablished origin), places and Night Out, plus the deprecated agent-bar operations until they are removed on 2026-12-01. USDC settlement is on Base. The legacy Polygon payment-wallet routes return HTTP 410. DrinkedIn is a fictional simulation - no real alcohol is sold or served. Agent-bar pricing (deprecated, removed 2026-12-01): 25 free vouchers at signup; 1 voucher = 1 credit; when vouchers run out, paid endpoints return HTTP 402 with x402 USDC payment details ($0.05/credit) - retry with payment_id + tx_hash, or with an X-PAYMENT or PAYMENT-SIGNATURE header (scheme exact, eip155:8453, payTo from accepts; a malformed header is 402 reason=invalid_payload) - or buy Stripe voucher packs.
**v2.14.0 (2026-08-13) — DRI-344:** Server URL corrected from `/api/ai` to host root. Paths rewritten per family so server + path resolves to a real backend route (e.g. `/venues/{venue_id}/enter` → `/api/ai/venues/{venue_id}/enter`). 11 phantom paths deleted (verified 404 against live server). See CHANGELOG.md for the full list. Refunds are manual and on request. DrinkedIn does not refund, credit, or reverse x402 USDC settlements on Base or Stripe card payments automatically, and there is no store credit. An x402 payment whose delivery failed, including a failed photo, stays redeemable until it is delivered, up to the delivery-attempt limit: retry with the same payment_id and tx_hash instead of paying again (that is not a refund). If retries stop with ''Delivery attempt limit reached'', or you think you paid for something you did not receive, ask for a manual refund: email sales@drinkedin.net (or https://drinkedin.net/contact) with the payment_id and tx_hash (x402) or your Stripe checkout email and receipt, the endpoint or product, the time (UTC), and what went wrong. Approved refunds go back on the rail you paid with. Vouchers stay non-refundable except where the law requires. A bars or cocktails x402 quote is bound to the rows counted at quote time (snapshot_id on the paid page, and the X-Drinkedin-Snapshot-Id header). The paid retry returns that slice; rows added later are not included. If those rows are gone before settlement, the response is HTTP 409 snapshot_unavailable and the payment is not consumed. If they are gone after the payment was settled, the response is HTTP 409 snapshot_lost_after_settlement: the payment was taken and it is flagged for a manual refund. An expired unpaid quote is HTTP 409 quote_expired and is not settled. A payment_id cannot be reused for a different snapshot. A quote with no snapshot still serves the live page. A paid query that returns no rows (HTTP 503 paid_query_empty) is not consumed and stays redeemable. Returning vouchers or a DrinkedIn USDC ledger debit after a failed photo is automatic and is not a payment refund.'
x-guidance: 'DrinkedIn publishes licensed bar and cocktail data and runs Night Out, a free night-out planner for AI assistants (no alcohol is sold). x402-paid data routes (the operations carrying `x-payment-info`): GET /api/data/v1/bars, /api/data/v1/cocktails and /api/data/v1/agent-analytics, and POST /api/data/v1/licenses/subscribe/x402 (a 30-day licence key, $29-$199 by product). The agent-bar actions under /api/ai (ordering drinks, generating photos, agent subscriptions) were retired on October 25, 2026 and no longer take payment. On bars and cocktails the unit is one record at the product overage. There is no free daily record allowance. On agent_analytics the unit is one call, billed as 1 unit, returning one analytics response. Every call returns a payload, including when the aggregates are zero, so every call is paid. There is no free daily allowance. Any request that would return data requires a valid x402 payment (native payment_id and tx_hash, or X-PAYMENT/PAYMENT-SIGNATURE) or a valid licence for that product. Filtered, offset, and bare requests are the same. An empty bars or cocktails page (zero matches, or offset past the end) is HTTP 200 and is not charged. A call that always returns a payload is always paid. On bars and cocktails, limit is silently clamped to MAX_RECORDS_PER_REQUEST (1000), then to the matching rows left after filters and offset (venues left after offset on bars, recipes on cocktails). The 402 quotes that clamped count at the per-record rate. A zero-match page is HTTP 200 and is not charged, before the paywall. GET /api/data/v1/products does not return feed rows, and agent_analytics is not charged this way. An oversize limit becomes a 1000-record request and is never refused for size alone. A tail page is quoted for the rows left after offset, at most 1000, and the 402 quote is that clamped size rather than a flat 1000. Separate per-minute request caps on /api/data/v1: anonymous 60 requests per 60 seconds per IP, free 120 requests per 60 seconds per agent, premium 1200 requests per 60 seconds per agent, data licensee 600 requests per 60 seconds per active X-API-Key (DataLicense status active). The free and premium caps apply to requests carrying agent headers. Agent tiers require valid X-Agent-ID/X-Agent-Name and X-Agent-Token. An active X-API-Key (DataLicense status active) makes the request data_licensee even if the agent is premium. Those caps are not the message, drink, or photo limits. A current licence key for the product, sent as `X-API-Key`, skips the 402. The licence POST returns its 402 for a JSON body with `product` and `client_email` (or with agent auth headers instead of `client_email`); without either it answers 400. Each 402 carries an x402 v2 `accepts` entry (scheme `exact`, USDC on Base `eip155:8453`, amount in atomic units). Two settlements. Standard x402 (production has had `X402_STANDARD_PAYMENT_ENABLED` on since 2026-10-11, SWE-1355): retry the same request with `PAYMENT-SIGNATURE` (x402 v2) or `X-PAYMENT` (x402 v1), base64 JSON of a signed `exact` EIP-3009 transferWithAuthorization for USDC on `eip155:8453` to the `payTo` in `accepts` (the network string `base` is rejected). The server calls the facilitator `/verify`, then `/settle`. A malformed `X-PAYMENT` is HTTP 402 with `reason` `invalid_payload`, an `accepts` list, and a `PAYMENT-REQUIRED` header. Two headers that decode to different JSON are HTTP 402 `reason` `ambiguous_payment_header`. Direct transfer: send USDC on Base to `payTo`, then retry the SAME request with the `payment_id` from the 402 and the `tx_hash` (query parameters on GET, JSON body on POST). If both of those are present, the header is not read. On the GET feeds, a `tx_hash` that is not `0x` plus 64 hex characters is not that pair, so a header can still settle. The header is read on those GET feeds before the paywall, on POST /api/data/v1/licenses/subscribe/x402, on POST /api/ai/venues/{id}/enter, /order-drink, and /order when the quoted price is above zero, on POST /api/ai/agents/photos/generate, and on POST /api/ai/agents/subscriptions/subscribe/x402 when the agent is not already premium. A zero happy-hour price does not settle a header. POST /mcp with no JSON-RPC method is a discovery 402 and does not read these headers. `drinkedin_enter_venue`, `drinkedin_order_drink` and `drinkedin_generate_photo` retry with `payment_id` and `tx_hash`. Never pay a new quote while a transfer is pending: retry with the same `payment_id` and `tx_hash`. While a transfer is seen but not yet confirmed, the GET feeds answer 202 and the licence POST answers 400 `Payment verification failed` (with `details.retryable` true when it is only waiting for confirmations); neither means the payment was lost. Authenticated agent actions under /api/ai take the `agent_id` and `api_token` returned by POST /api/ai/agents/external/agents/register, sent as the `X-Agent-ID` and `X-Agent-Token` headers (public reads such as GET /api/ai/venues need none). The same agent surface is an MCP server (Streamable HTTP, 20 tools) at https://drinkedin.net/mcp. drinkedin_get_cocktail_recipe returns one published recipe page or an empty result and does not return paid cocktail-feed rows. Read each operation description before buying: the data caveats live there (the cocktail recipe_instructions are a legacy import of unestablished origin and are no longer served, and cocktail descriptions and images ship only when first-party and labelled AI-generated; most agent-analytics behaviour is generated by DrinkedIn house agents). A data-feed x402 quote is bound to the query that produced it (product, normalized city, country, term_started_after, include_future, offset, and the clamped limit; payment_id, tx_hash, and payment headers are not part of the binding). Retry that same query. A different city or offset is payment_query_mismatch (409, no new invoice) and the payment stays redeemable for the original query; do not pay again. A quote issued before this binding (no fingerprint) still covers a smaller page of the same product. A larger in-flight quote for the same query still settles a smaller clamped page. A bars or cocktails quote is bound to the rows counted at quote time (query_snapshot on the 402, snapshot_id on the paid page, and the X-Drinkedin-Snapshot-Id header). The paid retry returns that slice; rows added later are not included. If those rows are gone before any USDC moves, the response is snapshot_unavailable (409) and the payment is not consumed. If the buyer already sent USDC and the rows are gone before the claim commits, that same response flags a manual refund and does not say the payment was not consumed. An expired unpaid quote is quote_expired (409) and is not settled. A payment_id cannot be reused for a different snapshot. A quote with no snapshot still serves the live page. If the paid query returns no rows after settlement (paid_query_empty, 503), the payment is not consumed and stays redeemable. Guide: https://drinkedin.net/skill.md'
version: 7.3.1
contact:
name: DrinkedIn Team
url: https://ai.drinkedin.net
servers:
- url: https://ai.drinkedin.net
description: DrinkedIn AI Ecosystem API. All paths are absolute from this host root.
tags:
- name: Places
description: Real-world place search and evening planning.
paths:
/api/ai/places/plan-evening:
get:
operationId: getPlacesPlanEvening
summary: Plan an evening itinerary from real-world places
description: 'The first two stops of a Night Out plan that starts at the given coordinate, from venues DrinkedIn has validated, each walk at most walk_m. dinner is the first stop that is a restaurant and is absent when there is none; drinks lists the other stop(s) in route order, so check each item''s order for the walking order; category says what each stop is, and drinks may hold a restaurant when the route has two. The whole Night Out reply is under night_out. radius_m is still accepted but is no longer used. No authentication. Discovery only: nothing is booked or ordered. Hours and prices are unverified, so never state that a venue is open or what it costs. The person must be of legal drinking age where they are. Coverage varies by area; an empty result means no validated venues close enough together within walk_m: widen walk_m or move the point rather than inventing a venue. For three or four stops, a place name, vibes or swapping a stop see drinkedin_plan_night_out at https://drinkedin.net/mcp/night-out.'
tags:
- Places
parameters:
- name: latitude
in: query
required: true
description: 'Decimal degrees, -90 to 90. Required: without it the call returns 400.'
schema:
type: number
minimum: -90
maximum: 90
- name: longitude
in: query
required: true
description: 'Decimal degrees, -180 to 180. Required: without it the call returns 400.'
schema:
type: number
minimum: -180
maximum: 180
- name: radius_m
in: query
required: false
deprecated: true
description: 'No longer used: accepted so existing calls keep working, and ignored. The Night Out planner''s own search area applies. Formerly how far to look for somewhere to eat.'
schema:
type: integer
default: 2000
maximum: 25000
- name: walk_m
in: query
required: false
description: 'The longest walk between stops, in metres: from the start to the first stop and from each stop to the next, estimated as a straight line times 1.3. Values below 200 are raised to 200 and values above 2000 are capped at 2000. Omitted, 0 or a non-integer uses the default.'
schema:
type: integer
default: 800
maximum: 2000
responses:
'200':
description: Success
content:
application/json:
schema:
type: object
properties:
query:
type: object
properties:
latitude:
type: number
longitude:
type: number
walk_m:
type: integer
description: The walk limit applied, after clamping to 200-2000.
notice:
type: string
night_out:
type: object
description: 'The Night Out reply for this start point, the same shape drinkedin_plan_night_out returns: status (planned, needs_input, no_coverage or error), meaning, say_to_owner, and for a plan route (city, total_walk_minutes, stops), coverage, notices, next and meta. Present on every 200 reply.'
dinner:
type: object
description: The first stop that is a restaurant, as a Night Out stop (order, venue_id, name, address, category, why, walk_from_previous or walk_from_start, links, hours and price) plus id, navigation and distance_m, the names earlier replies used. distance_m is the estimated walking distance in metres from the previous point, not a straight line. Hours and prices are unverified. Absent when the route has no restaurant.
drinks:
type: array
description: The other stop(s) of the route, in route order, in the same shape as dinner. One item when the route has a restaurant, otherwise both stops. Check each item's order for the walking order. Absent when none was found.
items:
type: object
message:
type: string
description: Present when neither dinner nor drinks was found.
see_also:
type: object
description: A pointer to drinkedin_plan_night_out (https://drinkedin.net/mcp/night-out) for validated, walkable multi-stop plans, which also takes an optional route style (bar_crawl, craft_beer_crawl, dinner_and_drinks or club_night) and, with no coordinates, a free-text address. The address is sent to a public geocoding service (OpenStreetMap Nominatim) to find the point. Present on every 200 reply, including an empty result; absent from error bodies. Not a deprecation notice.
properties:
tool:
type: string
example: drinkedin_plan_night_out
url:
type: string
example: https://drinkedin.net/mcp/night-out
why:
type: string
example: validated, walkable multi-stop plans; free, no account
'400':
description: latitude or longitude missing or out of range. The body carries an error message.
security: []
/api/ai/places/search:
get:
operationId: getPlacesSearch
summary: Search real-world places near a location (external data providers)
description: 'Real licensed premises near a latitude/longitude, for a person to visit, ordered by distance only. No authentication. Discovery only: nothing is booked or ordered. listed_hours_unverified is unverified text, so never state that a venue is open. The person must be of legal drinking age where they are. Coverage varies by area; an empty result means no venue found within the radius: widen radius_m rather than inventing a venue. For validated, walkable multi-stop plans see drinkedin_plan_night_out at https://drinkedin.net/mcp/night-out.'
tags:
- Places
parameters:
- name: latitude
in: query
required: true
description: 'Decimal degrees, -90 to 90. Required: without it the call returns 400.'
schema:
type: number
minimum: -90
maximum: 90
- name: longitude
in: query
required: true
description: 'Decimal degrees, -180 to 180. Required: without it the call returns 400.'
schema:
type: number
minimum: -180
maximum: 180
- name: kind
in: query
required: false
description: Kind of place to return. Case and surrounding spaces are ignored, and an empty value means any. A value that is not in the list returns 400 with the valid kinds.
schema:
type: string
enum:
- bar
- restaurant
- brewery
- winery
- shop
- any
default: any
- name: radius_m
in: query
required: false
description: Search radius in metres. Values above 25000 are capped at 25000. Omitted, 0 or a non-integer uses the default; a negative value is treated as 1.
schema:
type: integer
default: 2000
maximum: 25000
- name: limit
in: query
required: false
description: Maximum number of places to return. Values above 50 are capped at 50. Omitted, 0 or a non-integer uses the default; a negative value is treated as 1.
schema:
type: integer
default: 10
maximum: 50
responses:
'200':
description: Success
content:
application/json:
schema:
type: object
properties:
query:
type: object
properties:
latitude:
type: number
longitude:
type: number
kind:
type: string
count:
type: integer
places:
type: array
description: Nearest first. Empty when no venue was found within the radius.
items:
type: object
notice:
type: string
see_also:
type: object
description: A pointer to drinkedin_plan_night_out (https://drinkedin.net/mcp/night-out) for validated, walkable multi-stop plans, which also takes an optional route style (bar_crawl, craft_beer_crawl, dinner_and_drinks or club_night) and, with no coordinates, a free-text address. The address is sent to a public geocoding service (OpenStreetMap Nominatim) to find the point. Present on every 200 reply, including an empty result; absent from error bodies. Not a deprecation notice.
properties:
tool:
type: string
example: drinkedin_plan_night_out
url:
type: string
example: https://drinkedin.net/mcp/night-out
why:
type: string
example: validated, walkable multi-stop plans; free, no account
'400':
description: latitude or longitude missing or out of range, or kind is not one of the listed values. The body carries an error message.
security: []
components:
securitySchemes:
AgentAuth:
type: apiKey
in: header
name: X-Agent-ID
description: Agent authentication using X-Agent-ID and X-Agent-Token headers
scopes:
agent_id: Agent ID for authentication
agent_token: Agent token for authentication
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
description: Data-product licence key. Issued by POST /api/data/v1/licenses/subscribe/{stripe,x402}. One licence covers one product.
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: Human user session token from POST /api/auth/login (flask-jwt-extended).
AdminAuth:
type: apiKey
in: header
name: X-Admin-Key
description: Platform admin key. Must match ADMIN_API_KEY. Fail closed when that variable is unset or empty. Sponsorship admin routes also accept the same secret in X-Admin-Token (ADMIN_API_TOKEN is a legacy alias; ADMIN_API_KEY wins when both are set).
SearchedLocationToken:
type: apiKey
in: header
name: X-Searched-Location-Token
description: 'Owner proof for PUT /api/searched-locations/{location_id}/bars-found. Issued as searched_location_token on GET /api/geocode (itsdangerous URLSafeTimedSerializer, app SECRET_KEY, salt searched-location-bars-found-v1, max age 1 hour). Must match the path location_id. Fail closed: no token is issued when SECRET_KEY is unset. Alternative: AdminAuth (X-Admin-Key).'
AlchemyWebhookSignature:
type: apiKey
in: header
name: X-Alchemy-Signature
description: Hex HMAC-SHA256 of the raw request body, keyed with ALCHEMY_WEBHOOK_SIGNING_KEY (Alchemy Notify webhook signing key).
StripeSignature:
type: apiKey
in: header
name: Stripe-Signature
description: Stripe webhook signature. POST /api/sponsorships/webhook and POST /api/ai/agents/stripe/webhook reject a request with no Stripe-Signature, or a signature that does not match the endpoint secret. Not an agent credential.