{
  "openapi": "3.1.0",
  "info": {
    "title": "Bidali Gift Card API",
    "version": "0.1.0-draft",
    "summary": "Draft, subject to change. Quote, order and deliver gift cards.",
    "description": "DRAFT — subject to change. Early access only.\n\nQuote, order and deliver gift cards from 4,700+ brands in 150+ countries.\nAuthenticate with a Bearer API key. Send an `Idempotency-Key` header on\n`POST /v1/orders` so retries never buy the same gift card twice.\n",
    "contact": {
      "name": "Bidali",
      "url": "https://www.bidali.com/products/gift-card-api",
      "email": "sales@bidali.com"
    }
  },
  "servers": [
    {
      "url": "https://sandbox.api.bidali.invalid",
      "description": "Sandbox host to be announced at early access"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Catalog"
    },
    {
      "name": "Orders"
    },
    {
      "name": "Account"
    },
    {
      "name": "Webhooks"
    }
  ],
  "paths": {
    "/v1/countries": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "operationId": "listCountries",
        "summary": "List countries where gift cards are sold",
        "description": "Returns every country where Bidali sells gift cards, as ISO 3166-1 alpha-2 codes with display names. Use a country code to filter the catalog.",
        "responses": {
          "200": {
            "description": "Countries",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Country"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/currencies": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "operationId": "listCurrencies",
        "summary": "List supported currencies",
        "description": "Returns the currencies gift cards are priced in, with the number of minor units each one uses, so amounts can be shown and rounded correctly.",
        "responses": {
          "200": {
            "description": "Currencies",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Currency"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/brands": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "operationId": "listBrands",
        "summary": "Search the catalog",
        "description": "Searches the gift card catalog. Filter by country and category, and page through results with the cursor returned by the previous page.",
        "parameters": [
          {
            "name": "country",
            "in": "query",
            "description": "ISO 3166-1 alpha-2 country code.",
            "schema": {
              "type": "string",
              "examples": [
                "CA"
              ]
            }
          },
          {
            "name": "category",
            "in": "query",
            "description": "Category slug, for example `groceries`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Pagination cursor from a previous response.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Brands",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Brand"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/brands/{slug}": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "operationId": "getBrand",
        "summary": "Get one brand",
        "description": "Returns one brand with its available denominations, terms and redemption instructions. Call it before quoting so the amount you ask for is one the brand sells.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The brand slug, as returned by the catalog search (for example `starbucks-us`)."
          }
        ],
        "responses": {
          "200": {
            "description": "Brand with denominations, terms and redemption instructions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Brand"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/quotes": {
      "post": {
        "tags": [
          "Orders"
        ],
        "operationId": "createQuote",
        "summary": "Price a gift card before you buy it",
        "description": "Prices a gift card for a brand, amount and currency before you buy it. The quote states what the card costs you and when the price expires; pass its id to create the order.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "brand",
                  "amount",
                  "currency"
                ],
                "properties": {
                  "brand": {
                    "type": "string",
                    "description": "Brand slug."
                  },
                  "amount": {
                    "type": "number",
                    "description": "Gift card face value."
                  },
                  "currency": {
                    "type": "string",
                    "description": "ISO 4217 currency code."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Quote. It expires at `expires_at`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/Invalid"
          }
        }
      }
    },
    "/v1/orders": {
      "post": {
        "tags": [
          "Orders"
        ],
        "operationId": "createOrder",
        "summary": "Order a gift card from a quote",
        "description": "Buys the gift card priced by a quote and delivers it by email, SMS, link or inline in the response. Send an Idempotency-Key header so a retried request never buys the same card twice.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "A unique value per order. Retries with the same key return the original order.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "quote_id",
                  "delivery"
                ],
                "properties": {
                  "quote_id": {
                    "type": "string"
                  },
                  "delivery": {
                    "$ref": "#/components/schemas/Delivery"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Order created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "The Idempotency-Key was reused with a different request."
          },
          "422": {
            "$ref": "#/components/responses/Invalid"
          }
        }
      }
    },
    "/v1/orders/{id}": {
      "get": {
        "tags": [
          "Orders"
        ],
        "operationId": "getOrder",
        "summary": "Get an order and its status",
        "description": "Returns an order and its status. Once the order is fulfilled the response includes the gift card itself.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The order id returned when the order was created."
          }
        ],
        "responses": {
          "200": {
            "description": "Order",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/balance": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getBalance",
        "summary": "Get your prepaid balance",
        "description": "Returns the prepaid balance orders are drawn from, with recent statement lines for reconciliation.",
        "responses": {
          "200": {
            "description": "Balance and recent statement lines",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "available": {
                      "type": "number"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "statement": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "description": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/webhooks": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "createWebhook",
        "summary": "Register a webhook",
        "description": "Registers a URL to be called when an order is fulfilled or fails, or when the prepaid balance runs low.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url",
                  "events"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "order.fulfilled",
                        "order.failed",
                        "balance.low"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/Invalid"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API key"
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid API key."
      },
      "NotFound": {
        "description": "Not found."
      },
      "Invalid": {
        "description": "The request body failed validation."
      }
    },
    "schemas": {
      "Country": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "Currency": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "minor_units": {
            "type": "integer"
          }
        }
      },
      "Brand": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "countries": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "denominations": {
            "type": "array",
            "items": {
              "type": "number"
            }
          },
          "terms": {
            "type": "string"
          },
          "redemption": {
            "type": "string"
          }
        }
      },
      "Quote": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "brand": {
            "type": "string"
          },
          "amount": {
            "type": "number"
          },
          "currency": {
            "type": "string"
          },
          "price": {
            "type": "number"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Delivery": {
        "type": "object",
        "required": [
          "method"
        ],
        "properties": {
          "method": {
            "type": "string",
            "enum": [
              "email",
              "sms",
              "link",
              "inline"
            ]
          },
          "to": {
            "type": "string",
            "description": "Email address or phone number. Omit for `link` and `inline`."
          }
        }
      },
      "Order": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "fulfilled",
              "failed"
            ]
          },
          "quote_id": {
            "type": "string"
          },
          "delivery": {
            "$ref": "#/components/schemas/Delivery"
          },
          "gift_card": {
            "type": "object",
            "description": "Present once fulfilled. For `inline` delivery it holds the code.",
            "properties": {
              "url": {
                "type": "string",
                "format": "uri"
              },
              "code": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  },
  "externalDocs": {
    "description": "Gift Card API overview and early access",
    "url": "https://www.bidali.com/products/gift-card-api"
  }
}
