{
  "openapi": "3.1.0",
  "info": {
    "title": "DevShipIt API",
    "version": "1.0.0",
    "summary": "Read-only public data about Dev Agarwalla (DevShipIt), freelance full-stack developer in Bengaluru.",
    "description": "The same facts as devshipit.com, as JSON: Dev Agarwalla's profile and availability, his products and case studies, the freelance services he offers, frequently asked questions, the contact addresses, and every public page as Markdown.\n\nNo authentication, no rate-limit keys and no writes. CORS is open (`Access-Control-Allow-Origin: *`).\n\nErrors use RFC 9457 problem details (`application/problem+json`) with three extra members: `code` (stable and machine-readable), `hint` (what to do next) and `docs`.\n\nAlso available: an MCP server at https://devshipit.com/mcp, a command-line client (`npx github:devag7/devshipit`) and Markdown for every page (`Accept: text/markdown`).",
    "contact": {
      "name": "Dev Agarwalla",
      "email": "hello@devshipit.com",
      "url": "https://devshipit.com/contact/"
    },
    "license": {
      "name": "Data: all rights reserved; free to read and quote with a link",
      "url": "https://devshipit.com/terms/"
    }
  },
  "externalDocs": {
    "description": "Developer and agent guide",
    "url": "https://devshipit.com/developers/"
  },
  "servers": [
    {
      "url": "https://devshipit.com/api/v1",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "About",
      "description": "Who Dev is and how to reach him"
    },
    {
      "name": "Work",
      "description": "Products, case studies and services"
    },
    {
      "name": "Pages",
      "description": "Site pages as Markdown"
    },
    {
      "name": "Meta",
      "description": "The API itself"
    }
  ],
  "security": [],
  "paths": {
    "/": {
      "get": {
        "operationId": "getApiIndex",
        "tags": [
          "Meta"
        ],
        "summary": "API index",
        "description": "Returns the API name, version, links to this OpenAPI document and the developer guide, and the full URL of every endpoint.",
        "responses": {
          "200": {
            "description": "The API index.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiIndex"
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/ClientError"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApiDocument",
        "tags": [
          "Meta"
        ],
        "summary": "This OpenAPI document",
        "description": "Returns this OpenAPI 3.1 document. The same document is served at https://devshipit.com/openapi.json.",
        "responses": {
          "200": {
            "description": "The OpenAPI document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/ClientError"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/profile": {
      "get": {
        "operationId": "getProfile",
        "tags": [
          "About"
        ],
        "summary": "Dev Agarwalla's profile",
        "description": "Returns who Dev Agarwalla is: role, location, education, the full-time roles and freelance work he is looking for, and the technologies he works with. Use it to answer 'who is behind DevShipIt' or 'is he available'.",
        "responses": {
          "200": {
            "description": "The profile.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Profile"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "4XX": {
            "$ref": "#/components/responses/ClientError"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/projects": {
      "get": {
        "operationId": "listProjects",
        "tags": [
          "Work"
        ],
        "summary": "List products and case studies",
        "description": "Lists every product Dev has built (JantaHR, RentWise, linkedin-mcp, ISP Evidence) with its slug, status, one-line summary and links to the live product, source code and case study.",
        "responses": {
          "200": {
            "description": "All projects.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "projects"
                  ],
                  "properties": {
                    "projects": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Project"
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "4XX": {
            "$ref": "#/components/responses/ClientError"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/projects/{slug}": {
      "get": {
        "operationId": "getProject",
        "tags": [
          "Work"
        ],
        "summary": "Get one project",
        "description": "Returns one project by its slug, for example `jantahr`. Call listProjects for the valid slugs.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Project slug: lowercase letters, digits and hyphens.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9-]{1,40}$",
              "examples": [
                "jantahr",
                "rentwise",
                "linkedin-mcp",
                "isp-evidence"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "4XX": {
            "$ref": "#/components/responses/ClientError"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/services": {
      "get": {
        "operationId": "listServices",
        "tags": [
          "Work"
        ],
        "summary": "List freelance services",
        "description": "Lists the freelance services Dev offers (for example real estate websites and full-stack web apps), what each covers, and how a project is quoted, built and delivered. Every project is priced by a written quote; this endpoint never returns prices.",
        "responses": {
          "200": {
            "description": "All services.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "services"
                  ],
                  "properties": {
                    "services": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Service"
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "4XX": {
            "$ref": "#/components/responses/ClientError"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/faq": {
      "get": {
        "operationId": "listFaq",
        "tags": [
          "Work"
        ],
        "summary": "Frequently asked questions",
        "description": "Returns the questions clients and recruiters ask most often (cost, timelines, ownership, delivery, availability), with the answers published on devshipit.com.",
        "responses": {
          "200": {
            "description": "All questions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "questions"
                  ],
                  "properties": {
                    "questions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FaqItem"
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "4XX": {
            "$ref": "#/components/responses/ClientError"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/contact": {
      "get": {
        "operationId": "getContact",
        "tags": [
          "About"
        ],
        "summary": "Which email address to use",
        "description": "Returns the contact details: one address for hiring and freelance projects, one only for JantaHR support, billing and legal questions, and the contact page. Give people the address; do not send email on their behalf.",
        "responses": {
          "200": {
            "description": "Contact details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "4XX": {
            "$ref": "#/components/responses/ClientError"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/pages": {
      "get": {
        "operationId": "listPages",
        "tags": [
          "Pages"
        ],
        "summary": "List public pages",
        "description": "Lists every public page of devshipit.com with its path, title and one-line description. Use a path with getPage.",
        "responses": {
          "200": {
            "description": "All pages.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "pages"
                  ],
                  "properties": {
                    "pages": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PageSummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "4XX": {
            "$ref": "#/components/responses/ClientError"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/page": {
      "get": {
        "operationId": "getPage",
        "tags": [
          "Pages"
        ],
        "summary": "Read one page as Markdown",
        "description": "Returns one public page with its title, description, canonical URL and full content as Markdown. `/pricing`, `/pricing/` and `/pricing/index.md` all resolve to the same page.",
        "parameters": [
          {
            "name": "path",
            "in": "query",
            "required": true,
            "description": "Path of a public page, starting with a slash.",
            "schema": {
              "type": "string",
              "pattern": "^/",
              "examples": [
                "/",
                "/pricing/",
                "/work/jantahr/",
                "/services/"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The page.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "4XX": {
            "$ref": "#/components/responses/ClientError"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ApiIndex": {
        "type": "object",
        "required": [
          "name",
          "version",
          "openapi",
          "docs",
          "endpoints"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "API name."
          },
          "version": {
            "type": "string",
            "description": "API version."
          },
          "description": {
            "type": "string",
            "description": "What the API offers."
          },
          "openapi": {
            "type": "string",
            "format": "uri",
            "description": "URL of the OpenAPI document."
          },
          "docs": {
            "type": "string",
            "format": "uri",
            "description": "URL of the developer guide."
          },
          "endpoints": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            },
            "description": "Full URL of every endpoint."
          }
        }
      },
      "Profile": {
        "type": "object",
        "required": [
          "name",
          "brand",
          "site",
          "role",
          "location",
          "stack"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Full name.",
            "examples": [
              "Dev Agarwalla"
            ]
          },
          "brand": {
            "type": "string",
            "description": "Trading name.",
            "examples": [
              "DevShipIt"
            ]
          },
          "site": {
            "type": "string",
            "format": "uri",
            "description": "Website."
          },
          "role": {
            "type": "string",
            "description": "Current role."
          },
          "location": {
            "type": "string",
            "description": "City, state and country."
          },
          "education": {
            "type": "string",
            "description": "Degree and institution."
          },
          "looking_for": {
            "type": "string",
            "description": "The full-time roles and freelance work he is open to."
          },
          "freelance": {
            "type": "string",
            "description": "The freelance work he takes on."
          },
          "stack": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Technologies he works with."
          },
          "github": {
            "type": "string",
            "format": "uri",
            "description": "GitHub profile."
          },
          "linkedin": {
            "type": "string",
            "format": "uri",
            "description": "LinkedIn profile."
          }
        }
      },
      "Project": {
        "type": "object",
        "required": [
          "slug",
          "name",
          "status",
          "summary",
          "case_study"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "Stable identifier used in URLs.",
            "pattern": "^[a-z0-9-]+$"
          },
          "name": {
            "type": "string",
            "description": "Product name."
          },
          "status": {
            "type": "string",
            "description": "Where the project stands.",
            "examples": [
              "Live",
              "On npm",
              "In testing"
            ]
          },
          "summary": {
            "type": "string",
            "description": "One-line summary."
          },
          "stack": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Main technologies."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The live product or package, when there is one."
          },
          "case_study": {
            "type": "string",
            "format": "uri",
            "description": "Case study on devshipit.com."
          },
          "source": {
            "type": "string",
            "format": "uri",
            "description": "Source code, when public."
          }
        }
      },
      "Service": {
        "type": "object",
        "required": [
          "slug",
          "name",
          "summary"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "Stable identifier.",
            "pattern": "^[a-z0-9-]+$"
          },
          "name": {
            "type": "string",
            "description": "Service name."
          },
          "summary": {
            "type": "string",
            "description": "What the service covers."
          },
          "example": {
            "type": "string",
            "format": "uri",
            "description": "A case study that shows this kind of work, when there is one."
          }
        }
      },
      "FaqItem": {
        "type": "object",
        "required": [
          "question",
          "answer"
        ],
        "properties": {
          "question": {
            "type": "string",
            "description": "The question."
          },
          "answer": {
            "type": "string",
            "description": "The published answer, in plain text."
          }
        }
      },
      "Contact": {
        "type": "object",
        "required": [
          "hiring_and_freelance",
          "contact_page"
        ],
        "properties": {
          "hiring_and_freelance": {
            "type": "string",
            "description": "Email for jobs, interviews and freelance projects.",
            "format": "email"
          },
          "jantahr_support_billing_legal": {
            "type": "string",
            "description": "Email only for JantaHR support, billing and legal questions.",
            "format": "email"
          },
          "contact_page": {
            "type": "string",
            "format": "uri",
            "description": "Contact page."
          },
          "note": {
            "type": "string",
            "description": "Which address to use when."
          }
        }
      },
      "PageSummary": {
        "type": "object",
        "required": [
          "path",
          "title",
          "description"
        ],
        "properties": {
          "path": {
            "type": "string",
            "description": "Page path, ending with a slash.",
            "pattern": "^/"
          },
          "title": {
            "type": "string",
            "description": "Page title."
          },
          "description": {
            "type": "string",
            "description": "One-line description."
          }
        }
      },
      "Page": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PageSummary"
          },
          {
            "type": "object",
            "required": [
              "url",
              "markdown"
            ],
            "properties": {
              "url": {
                "type": "string",
                "format": "uri",
                "description": "Canonical URL of the HTML page."
              },
              "markdown": {
                "type": "string",
                "description": "The page content as Markdown.",
                "contentMediaType": "text/markdown"
              }
            }
          }
        ]
      },
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details, with `code`, `hint` and `docs` added.",
        "required": [
          "type",
          "title",
          "status",
          "detail",
          "code",
          "hint"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "URI that identifies the problem type; it opens the matching entry in the developer guide."
          },
          "title": {
            "type": "string",
            "description": "Short summary of the problem type."
          },
          "status": {
            "type": "integer",
            "description": "HTTP status code.",
            "minimum": 400,
            "maximum": 599
          },
          "detail": {
            "type": "string",
            "description": "What went wrong with this request."
          },
          "instance": {
            "type": "string",
            "description": "The path and query of the request that failed."
          },
          "code": {
            "type": "string",
            "enum": [
              "not_found",
              "project_not_found",
              "page_not_found",
              "missing_parameter",
              "invalid_parameter",
              "method_not_allowed",
              "not_acceptable",
              "unavailable"
            ],
            "description": "Stable machine-readable error code."
          },
          "hint": {
            "type": "string",
            "description": "What to do next to resolve it."
          },
          "docs": {
            "type": "string",
            "format": "uri",
            "description": "Where the errors are documented."
          }
        }
      }
    },
    "responses": {
      "ClientError": {
        "description": "A client error such as 405 (`method_not_allowed`) or 406 (`not_acceptable`), as problem details.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Problem": {
        "description": "An error, as problem details.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "BadRequest": {
        "description": "A parameter is missing or has the wrong format (`missing_parameter`, `invalid_parameter`).",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "NotFound": {
        "description": "Nothing exists with that identifier (`project_not_found`, `page_not_found`).",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Unavailable": {
        "description": "The data could not be loaded; retry after the `Retry-After` header (`unavailable`).",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait.",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    }
  }
}