{
  "openapi": "3.1.0",
  "info": {
    "title": "Bankday API",
    "version": "1.0.0",
    "description": "Business days, holidays and settlement dates for payment rails, exchanges and countries.",
    "contact": {
      "url": "https://bankday.jacklehoff4866.workers.dev"
    }
  },
  "servers": [
    {
      "url": "https://bankday.jacklehoff4866.workers.dev"
    }
  ],
  "security": [
    {
      "bearer": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer"
      }
    },
    "responses": {
      "Error": {
        "description": "Error",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "paths": {
    "/v1/business-day": {
      "get": {
        "operationId": "business_day",
        "summary": "Is a date a business day?",
        "description": "Returns whether date is a business day on the calendar, why not (weekend or the holiday name, per calendar), and the next and previous business days. Alias: /v1/is-business-day.",
        "parameters": [
          {
            "name": "calendar",
            "in": "query",
            "required": true,
            "description": "Calendar id, or up to 10 comma-separated ids for a joint calendar (e.g. us-fed,eu-target2). Aliases such as nyse, ach, sepa, bacs are accepted.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "date",
            "in": "query",
            "required": true,
            "description": "YYYY-MM-DD",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "calendar": "us-fed",
                  "date": "2026-10-12",
                  "weekday": "Monday",
                  "business_day": false,
                  "closures": [
                    {
                      "calendar": "us-fed",
                      "reason": "holiday",
                      "name": "Columbus Day"
                    }
                  ],
                  "next_business_day": "2026-10-13",
                  "previous_business_day": "2026-10-09"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/today": {
      "get": {
        "operationId": "today",
        "summary": "Today's status in the calendar's time zone",
        "description": "Same as business-day for the current date in the calendar's local time zone (e.g. America/New_York for us-fed), plus the next five holidays within 120 days.",
        "parameters": [
          {
            "name": "calendar",
            "in": "query",
            "required": true,
            "description": "Calendar id, or up to 10 comma-separated ids for a joint calendar (e.g. us-fed,eu-target2). Aliases such as nyse, ach, sepa, bacs are accepted.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "timezone",
            "in": "query",
            "required": false,
            "description": "Optional IANA time zone overriding the calendar's own (default UTC for country calendars).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/business-days/add": {
      "get": {
        "operationId": "add",
        "summary": "Add or subtract business days",
        "description": "Moves days business days from date. The start date itself is not counted, so adding 1 business day to a Friday gives the following Monday (if it is open). Negative values go backwards.",
        "parameters": [
          {
            "name": "calendar",
            "in": "query",
            "required": true,
            "description": "Calendar id, or up to 10 comma-separated ids for a joint calendar (e.g. us-fed,eu-target2). Aliases such as nyse, ach, sepa, bacs are accepted.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "date",
            "in": "query",
            "required": true,
            "description": "YYYY-MM-DD",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "days",
            "in": "query",
            "required": true,
            "description": "Integer between -2600 and 2600",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "calendar": "us-fed",
                  "date": "2026-11-25",
                  "days": 2,
                  "result": "2026-11-30",
                  "result_status": {
                    "date": "2026-11-30",
                    "weekday": "Monday",
                    "business_day": true,
                    "closures": []
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/business-days/count": {
      "get": {
        "operationId": "count",
        "summary": "Count business days between two dates",
        "description": "Counts business days d with start &lt; d ≤ end (the usual \"how many business days until\"). Pass include_start=true to count start ≤ d ≤ end. Returns a negative number when end is before start.",
        "parameters": [
          {
            "name": "calendar",
            "in": "query",
            "required": true,
            "description": "Calendar id, or up to 10 comma-separated ids for a joint calendar (e.g. us-fed,eu-target2). Aliases such as nyse, ach, sepa, bacs are accepted.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "start",
            "in": "query",
            "required": true,
            "description": "YYYY-MM-DD",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "end",
            "in": "query",
            "required": true,
            "description": "YYYY-MM-DD",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include_start",
            "in": "query",
            "required": false,
            "description": "true | false (default false)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/business-days/next": {
      "get": {
        "operationId": "next",
        "summary": "Next (or previous) business day",
        "description": "The first business day strictly after date (default: today in the calendar's time zone). Use inclusive=true to return the date itself when it is a business day. /v1/business-days/previous works the same way backwards.",
        "parameters": [
          {
            "name": "calendar",
            "in": "query",
            "required": true,
            "description": "Calendar id, or up to 10 comma-separated ids for a joint calendar (e.g. us-fed,eu-target2). Aliases such as nyse, ach, sepa, bacs are accepted.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "date",
            "in": "query",
            "required": false,
            "description": "YYYY-MM-DD (default today)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "inclusive",
            "in": "query",
            "required": false,
            "description": "true | false (default false)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/business-days/roll": {
      "get": {
        "operationId": "roll",
        "summary": "Adjust a date with a business-day convention",
        "description": "Rolls a non-business date using ISDA-style conventions: following, preceding, modified_following (following, unless that crosses into the next month), modified_preceding. Business days are returned unchanged.",
        "parameters": [
          {
            "name": "calendar",
            "in": "query",
            "required": true,
            "description": "Calendar id, or up to 10 comma-separated ids for a joint calendar (e.g. us-fed,eu-target2). Aliases such as nyse, ach, sepa, bacs are accepted.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "date",
            "in": "query",
            "required": true,
            "description": "YYYY-MM-DD",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "convention",
            "in": "query",
            "required": false,
            "description": "following (default) | preceding | modified_following | modified_preceding",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/business-days": {
      "get": {
        "operationId": "list",
        "summary": "List business days in a range",
        "description": "All business days between start and end inclusive (max 2 years).",
        "parameters": [
          {
            "name": "calendar",
            "in": "query",
            "required": true,
            "description": "Calendar id, or up to 10 comma-separated ids for a joint calendar (e.g. us-fed,eu-target2). Aliases such as nyse, ach, sepa, bacs are accepted.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "start",
            "in": "query",
            "required": true,
            "description": "YYYY-MM-DD",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "end",
            "in": "query",
            "required": true,
            "description": "YYYY-MM-DD",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/holidays": {
      "get": {
        "operationId": "holidays",
        "summary": "List holidays",
        "description": "Holidays for a year (default: current year) or a date range up to 10 years. For joint calendars every calendar's holidays are listed with a calendar field.",
        "parameters": [
          {
            "name": "calendar",
            "in": "query",
            "required": true,
            "description": "Calendar id, or up to 10 comma-separated ids for a joint calendar (e.g. us-fed,eu-target2). Aliases such as nyse, ach, sepa, bacs are accepted.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "year",
            "in": "query",
            "required": false,
            "description": "e.g. 2027",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "start",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "end",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/fx/spot": {
      "get": {
        "operationId": "fx",
        "summary": "FX spot value date",
        "description": "Spot date for a currency pair traded on trade_date. T+2 by default, T+1 for USD/CAD, USD/TRY, USD/RUB, USD/PHP and USD/KZT. The intermediate day only needs to be a business day for the non-USD currency; the spot date must be a business day for both currencies and, for crosses such as EURGBP, also a USD business day. Currencies map to settlement calendars (USD → us-fed, EUR → eu-target2, GBP → uk-bacs, others → country).",
        "parameters": [
          {
            "name": "pair",
            "in": "query",
            "required": true,
            "description": "e.g. EURUSD, EUR/USD",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "trade_date",
            "in": "query",
            "required": true,
            "description": "YYYY-MM-DD",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "pair": "EURUSD",
                  "trade_date": "2026-11-25",
                  "spot_lag": 2,
                  "spot_date": "2026-11-27",
                  "calendars": {
                    "EUR": "eu-target2",
                    "USD": "us-fed"
                  },
                  "convention": "T+2 spot: ..."
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/ach/settlement": {
      "get": {
        "operationId": "ach",
        "summary": "US ACH settlement estimate",
        "description": "Estimates when an ACH entry settles at the Federal Reserve, given when you submit it. Same Day ACH uses the three FedACH windows (submission deadlines 10:30 a.m., 2:45 p.m. and 4:45 p.m. ET; settlement 1:00, 5:00 and 6:00 p.m. ET) and the $1,000,000 per-payment limit. Standard ACH settles the next settlement day at 8:30 a.m. ET. Your bank's (ODFI's) cut-offs are earlier than the Fed's.",
        "parameters": [
          {
            "name": "submitted_at",
            "in": "query",
            "required": false,
            "description": "ISO 8601 timestamp with offset (default: now)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "standard (default) | same_day",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "amount",
            "in": "query",
            "required": false,
            "description": "Optional, in USD; same-day entries over $1M fall back to standard",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/calendars": {
      "get": {
        "operationId": "calendars",
        "summary": "List calendars",
        "description": "All calendars with metadata (type, weekend days, time zone, coverage years, source). Filter with type and q. GET /v1/calendars/{id} returns one calendar plus this year's holidays.",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "rail | exchange | country",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search text",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/account": {
      "get": {
        "operationId": "account",
        "summary": "Your plan and usage",
        "description": "Plan, usage for the current month and usage history. Not counted toward your quota.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  }
}