{
  "openapi": "3.0.0",
  "paths": {
    "/partner-api/auth/onboard": {
      "post": {
        "description": "Create a new Juno user and organization linked to your partner integration. Optional taxSoftware and practiceManagementSoftware values are documented on the request schema.",
        "operationId": "PartnerAuthController_onboard",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OnboardUserDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OnboardUserResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "validationFailed": {
                    "summary": "Request validation failed",
                    "value": {
                      "statusCode": 400,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/onboard",
                      "method": "POST",
                      "message": [
                        "externalUserId should not be empty"
                      ]
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Partner organization already exists, or user is already linked to a partner",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "orgExists": {
                    "summary": "Partner organization already exists",
                    "value": {
                      "statusCode": 409,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/onboard",
                      "method": "POST",
                      "message": "Partner organization already exists"
                    }
                  },
                  "userLinked": {
                    "summary": "User already linked",
                    "value": {
                      "statusCode": 409,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/onboard",
                      "method": "POST",
                      "message": "User is already linked to a partner"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Error onboarding partner user",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "onboardFailed": {
                    "summary": "Error onboarding partner user",
                    "value": {
                      "statusCode": 500,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/onboard",
                      "method": "POST",
                      "message": "Error onboarding partner user"
                    }
                  }
                }
              }
            }
          }
        },
        "summary": "Onboard a new user and organization",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/partner-api/auth/onboard/current-user": {
      "post": {
        "description": "Link an existing Juno user (userId) to your partner organization. Use when the user already has a Juno account.",
        "operationId": "PartnerAuthController_onboardCurrentUser",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OnboardCurrentUserDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OnboardCurrentUserResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "External organization and user IDs are required, user not found, or validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "missingIds": {
                    "summary": "Missing external IDs",
                    "value": {
                      "statusCode": 400,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/onboard/current-user",
                      "method": "POST",
                      "message": "External organization and user IDs are required"
                    }
                  },
                  "userNotFound": {
                    "summary": "User not found",
                    "value": {
                      "statusCode": 400,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/onboard/current-user",
                      "method": "POST",
                      "message": "User not found"
                    }
                  },
                  "validationFailed": {
                    "summary": "Request validation failed",
                    "value": {
                      "statusCode": 400,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/onboard/current-user",
                      "method": "POST",
                      "message": [
                        "userId should not be empty"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Error onboarding user to partner",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "onboardCurrentUserFailed": {
                    "summary": "Error onboarding user to partner",
                    "value": {
                      "statusCode": 500,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/onboard/current-user",
                      "method": "POST",
                      "message": "Error onboarding user to partner"
                    }
                  }
                }
              }
            }
          }
        },
        "summary": "Onboard an existing TruePrep user to a partner org",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/partner-api/auth/pre-onboard": {
      "post": {
        "description": "Step 1 of partner onboarding (partner backend POST). Returns a `redirectUrl` for the user's browser (step 2 GET). After onboarding, Juno redirects the browser to your `callbackUrl` with tokens (step 3 GET). Requires X-Partner-Api-Key.\n\nTwo hosts are involved:\n- api.juno.tax — partner backend calls (POST/GET with API key + Bearer token)\n- app.juno.tax — user browser redirects during onboarding (GET only)\n\nRecommended onboarding flow:\n| Step | Method | Caller | Endpoint | Purpose |\n| 1 | POST | Partner backend | api.juno.tax/partner-api/auth/pre-onboard | Validate callback URL, get onboarding link |\n| 2 | GET | User browser | app.juno.tax/onboarding/partner?... | User signs up / logs in on Juno |\n| 3 | GET | User browser | <your callbackUrl>?accessToken&refreshToken | Deliver tokens to partner (current flow) |\n| 4 | GET | Partner backend | <your callbackUrl> handler | Extract tokens server-side |\n| 5 | POST | Partner backend | api.juno.tax/partner-api/... | API calls with Bearer + X-Partner-Api-Key |\n| 6 | POST | Partner backend | api.juno.tax/partner-api/auth/refresh | Renew tokens when access token expires |\n\nBrowser redirects use query parameters (withCallbackUrl, orgId, userId, partnerId). Partner API calls use JSON request bodies and Authorization headers — never put tokens in URLs for steps 5–6.\n\nPlanned change (future): step 3 will deliver a one-time authorization code via GET redirect; step 4 becomes POST /partner-api/auth/token (server-to-server code exchange), matching the OAuth authorization-code pattern already used by Juno Connect integrations.\n\nKnown limitation — token callback via query parameters:\nAfter onboarding, Juno redirects the user's browser to your callbackUrl with accessToken and refreshToken as URL query parameters. Tokens in URLs can be exposed via browser history, referrer headers, proxy logs, and server access logs.\n\nPlanned remediation (future):\nJuno will replace query-parameter token delivery with a short-lived, one-time authorization code that partners exchange server-to-server over HTTPS. Partners will no longer receive JWTs in callback URLs. Juno will provide a migration guide before the change ships.\n\nInterim mitigations (required until migration):\n- Register callback URLs on HTTPS origins only; Juno validates against your whitelisted callback origins\n- Handle the callback on your backend — never persist tokens in client-side storage from the redirect\n- Extract tokens immediately, then redirect the user to a clean URL with query parameters stripped\n- Never log callback URLs, query strings, or token values\n- Store tokens server-side only and use POST /partner-api/auth/refresh for renewal",
        "operationId": "PartnerAuthController_prepareOnboard",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrepareOnboardUserDTO"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrepareOnboardResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Unknown Callback URL, or request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "validationFailed": {
                    "summary": "Request validation failed",
                    "value": {
                      "statusCode": 400,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/pre-onboard",
                      "method": "POST",
                      "message": [
                        "callbackUrl should not be empty"
                      ]
                    }
                  },
                  "unknownCallback": {
                    "summary": "Unknown callback URL",
                    "value": {
                      "statusCode": 400,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/pre-onboard",
                      "method": "POST",
                      "message": "Unknown Callback URL 'https://yourapp.com/callback'. Please contact support."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing API key or Invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "missingApiKey": {
                    "summary": "Missing API key",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/pre-onboard",
                      "method": "POST",
                      "message": "Missing API key"
                    }
                  },
                  "invalidApiKey": {
                    "summary": "Invalid API key",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/pre-onboard",
                      "method": "POST",
                      "message": "Invalid API key"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "partner-api-key": []
          }
        ],
        "summary": "Pre-onboard a user and get an onboarding link",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/partner-api/auth/refresh": {
      "post": {
        "description": "Exchange a refresh token for a new access/refresh token pair. Tokens must be sent in the request body — never in URLs. Requires X-Partner-Api-Key.\n\nWhen the access token expires (~60 minutes), call POST /partner-api/auth/refresh before re-authenticating the user.\n\nRequest:\n- POST /partner-api/auth/refresh\n- Headers: X-Partner-Api-Key, Content-Type: application/json\n- Body: { \"refreshToken\": \"<stored refresh token>\" }\n\nSuccess (200):\n- Response: { \"accessToken\": \"...\", \"refreshToken\": \"...\" }\n- Both tokens are rotated — persist the new pair and discard the old refresh token\n- Retry the original API request once with the new accessToken\n\nRefresh failure (401):\n- Messages: \"Invalid refresh token\" or \"Token expired or invalid\"\n- The refresh token has expired (~30 days), been revoked, or does not match the active session\n- Clear stored tokens for that user and restart onboarding (POST /partner-api/auth/pre-onboard)\n\nImplementation notes:\n- On 401 from a partner API call, attempt refresh once, then retry the original request once\n- Do not loop refresh + retry indefinitely\n- Serialize refresh per user/session — concurrent refreshes invalidate earlier refresh tokens\n- Proactive refresh (e.g. before access token expiry) avoids mid-request failures",
        "operationId": "PartnerAuthController_refreshToken",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "refreshToken"
                ],
                "properties": {
                  "refreshToken": {
                    "type": "string",
                    "description": "Partner refresh token obtained from the onboarding callback"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerRefreshTokenResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing/Invalid API key, Invalid refresh token, or Token expired or invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "missingApiKey": {
                    "summary": "Missing API key",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/refresh",
                      "method": "POST",
                      "message": "Missing API key"
                    }
                  },
                  "invalidApiKey": {
                    "summary": "Invalid API key",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/refresh",
                      "method": "POST",
                      "message": "Invalid API key"
                    }
                  },
                  "invalidRefreshToken": {
                    "summary": "Invalid refresh token",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/refresh",
                      "method": "POST",
                      "message": "Invalid refresh token"
                    }
                  },
                  "tokenExpired": {
                    "summary": "Token expired or invalid",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/refresh",
                      "method": "POST",
                      "message": "Token expired or invalid"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "partner-api-key": []
          }
        ],
        "summary": "Refresh a partner user access token",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/partner-api/auth/authenticate": {
      "post": {
        "description": "Internal admin endpoint for linking partner users and issuing partner tokens. Requires administrator Bearer access token.",
        "operationId": "PartnerAuthController_authenticatePartnerUser",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AuthenticatePartnerUserDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthenticatePartnerUserResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "validationFailed": {
                    "summary": "Request validation failed",
                    "value": {
                      "statusCode": 400,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/authenticate",
                      "method": "POST",
                      "message": [
                        "externalUserId should not be empty"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "User not authenticated, invalid partner linkage, or insufficient role",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "notAuthenticated": {
                    "summary": "User not authenticated",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/authenticate",
                      "method": "POST",
                      "message": "User not authenticated"
                    }
                  },
                  "improperLinkage": {
                    "summary": "Improper partner linkage",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/authenticate",
                      "method": "POST",
                      "message": "Partner user and organization are not properly linked"
                    }
                  },
                  "userIdConflict": {
                    "summary": "External user ID conflict",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/authenticate",
                      "method": "POST",
                      "message": "External user ID is already linked to a different Juno user"
                    }
                  },
                  "orgMismatch": {
                    "summary": "User/org mismatch",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/authenticate",
                      "method": "POST",
                      "message": "User does not belong to the organization linked to this external org"
                    }
                  },
                  "orgConflict": {
                    "summary": "External org ID conflict",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/authenticate",
                      "method": "POST",
                      "message": "External org ID is already linked to a different Juno organization. Please contact your organization administrator."
                    }
                  },
                  "invalidState": {
                    "summary": "Invalid partner state",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/authenticate",
                      "method": "POST",
                      "message": "Invalid state: External user exists without corresponding organization"
                    }
                  },
                  "unauthorized": {
                    "summary": "Unauthorized access token or insufficient role",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/auth/authenticate",
                      "method": "POST",
                      "message": "Unauthorized"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "partner-access-token": []
          }
        ],
        "summary": "Authenticate a partner user (internal admin use)",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/partner-api/documents/secure-link": {
      "post": {
        "description": "Step 1 of document upload. Returns a presigned URL — PUT the file to signed_url, then pass url as s3Url in POST /documents/upload. documentId must match the ID used in the upload step. Requires X-Partner-Api-Key and Bearer access token.",
        "operationId": "DocumentController_generateUploadUrl",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerateUploadUrlDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GeneratePresignedUrlResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "validationFailed": {
                    "summary": "Request validation failed",
                    "value": {
                      "statusCode": 400,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/secure-link",
                      "method": "POST",
                      "message": [
                        "documentName should not be empty"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing/Invalid API key or Unauthorized access token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "missingApiKey": {
                    "summary": "Missing API key",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/secure-link",
                      "method": "POST",
                      "message": "Missing API key"
                    }
                  },
                  "invalidApiKey": {
                    "summary": "Invalid API key",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/secure-link",
                      "method": "POST",
                      "message": "Invalid API key"
                    }
                  },
                  "unauthorized": {
                    "summary": "Unauthorized access token",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/secure-link",
                      "method": "POST",
                      "message": "Unauthorized"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure (e.g. storage error). Uses the standard error envelope; message text varies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "partner-access-token": []
          },
          {
            "partner-api-key": []
          }
        ],
        "summary": "Generate a pre-signed S3 URL for document upload",
        "tags": [
          "Documents"
        ]
      }
    },
    "/partner-api/documents/upload": {
      "post": {
        "description": "Step 2 of document upload. Registers the S3 object from secure-link and starts processing. Field requirements depend on preparationType — see request schema for documentType, taxYear, client vs business fields. Requires X-Partner-Api-Key and Bearer access token.",
        "operationId": "DocumentController_confirmUpload",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UploadDocumentDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadDocumentResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed, client ID conflict, or document limit reached (trial)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "validationFailed": {
                    "summary": "Request validation failed",
                    "value": {
                      "statusCode": 400,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/upload",
                      "method": "POST",
                      "message": [
                        "s3Url should not be empty"
                      ]
                    }
                  },
                  "clientConflict": {
                    "summary": "Client ID conflict",
                    "value": {
                      "statusCode": 400,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/upload",
                      "method": "POST",
                      "message": "Client \"Jane Doe\" already exists with a different partner client ID"
                    }
                  },
                  "einConflict": {
                    "summary": "Business client EIN conflict",
                    "value": {
                      "statusCode": 400,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/upload",
                      "method": "POST",
                      "message": "Business client with EIN \"12-3456789\" already exists with a different partner client ID"
                    }
                  },
                  "trialDocLimit": {
                    "summary": "Trial document limit reached",
                    "value": {
                      "statusCode": 400,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/upload",
                      "method": "POST",
                      "message": "You have reached the 25 document limit per preparation."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing/Invalid API key or Unauthorized access token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "missingApiKey": {
                    "summary": "Missing API key",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/upload",
                      "method": "POST",
                      "message": "Missing API key"
                    }
                  },
                  "invalidApiKey": {
                    "summary": "Invalid API key",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/upload",
                      "method": "POST",
                      "message": "Invalid API key"
                    }
                  },
                  "unauthorized": {
                    "summary": "Unauthorized access token",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/upload",
                      "method": "POST",
                      "message": "Unauthorized"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Subscription not found, purchase/trial limit reached, or document limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "noSubscription": {
                    "summary": "Valid subscription not found",
                    "value": {
                      "statusCode": 403,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/upload",
                      "method": "POST",
                      "message": "Valid subscription not found"
                    }
                  },
                  "multipleSubscriptions": {
                    "summary": "Multiple subscriptions found",
                    "value": {
                      "statusCode": 403,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/upload",
                      "method": "POST",
                      "message": "Multiple subscriptions found"
                    }
                  },
                  "purchaseLimit": {
                    "summary": "Purchase limit reached",
                    "value": {
                      "statusCode": 403,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/upload",
                      "method": "POST",
                      "message": "Purchase limit reached"
                    }
                  },
                  "trialLimit": {
                    "summary": "Trial limit reached",
                    "value": {
                      "statusCode": 403,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/upload",
                      "method": "POST",
                      "message": "Trial limit reached"
                    }
                  },
                  "customerDocLimit": {
                    "summary": "Customer document limit reached",
                    "value": {
                      "statusCode": 403,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/upload",
                      "method": "POST",
                      "message": "You have reached the 200 document limit per preparation."
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Failed to create or find tax preparation, or internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "taxPrepRetries": {
                    "summary": "Tax preparation retries exhausted",
                    "value": {
                      "statusCode": 500,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/upload",
                      "method": "POST",
                      "message": "Failed to create or find tax preparation after 5 retries"
                    }
                  },
                  "clientRetries": {
                    "summary": "Client creation retries exhausted",
                    "value": {
                      "statusCode": 500,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/upload",
                      "method": "POST",
                      "message": "Failed to create or find client after 5 retries"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "partner-access-token": []
          },
          {
            "partner-api-key": []
          }
        ],
        "summary": "Register an uploaded document and trigger processing",
        "tags": [
          "Documents"
        ]
      }
    },
    "/partner-api/documents/status/{id}": {
      "get": {
        "description": "Returns the current processing state for a document you previously registered via POST /partner-api/documents/upload.\n\nPath parameter `id` is your `documentId` from the upload request (not Juno's internal ID). See the 200 response schema for `documentStatus` values. Requires X-Partner-Api-Key and Bearer access token.\n\nPoll after a successful POST /partner-api/documents/upload using the documentId you supplied.\n\nRecommended throttle (exponential backoff):\n- Interval sequence: 3s → 6s → 12s → 24s → 60s (cap)\n- Minimum interval: 3 seconds (do not poll sub-second)\n- Maximum interval: 60 seconds once processing is underway\n- Reset backoff when documentStatus changes (optional optimization)\n\nStop polling when documentStatus is terminal:\n- Success: PROCESSED\n- Failure: ERROR, ERROR_OCR, ERROR_POST_PROCESSING, ERROR_CLASSIFICATION, ERROR_BAD_INPUT_DATA, ERROR_NOT_RECENT_1040, ERROR_UNSUPPORTED_FORM\n- Other: ARCHIVED\n\nSafety limits:\n- Typical processing completes in 1–10 minutes; flag for manual review if still in-progress after 30 minutes\n- Stop automatic polling after 2 hours and surface a timeout to the user\n- When polling many documents, cap parallel requests (e.g. 10 concurrent) and stagger starts\n- No dedicated partner-api rate limiter exists today, but backoff keeps load reasonable (~20 req/min per document)\n\nWebhooks will replace polling when available.",
        "operationId": "DocumentController_getStatus",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Your documentId from POST /partner-api/documents/upload",
            "schema": {
              "example": "doc_abc123",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentStatusResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "DocumentId is required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "missingDocumentId": {
                    "summary": "DocumentId is required",
                    "value": {
                      "statusCode": 400,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/status/doc_abc123",
                      "method": "GET",
                      "message": "DocumentId is required"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing/Invalid API key or Unauthorized access token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "missingApiKey": {
                    "summary": "Missing API key",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/status/doc_abc123",
                      "method": "GET",
                      "message": "Missing API key"
                    }
                  },
                  "invalidApiKey": {
                    "summary": "Invalid API key",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/status/doc_abc123",
                      "method": "GET",
                      "message": "Invalid API key"
                    }
                  },
                  "unauthorized": {
                    "summary": "Unauthorized access token",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/status/doc_abc123",
                      "method": "GET",
                      "message": "Unauthorized"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Document not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "notFound": {
                    "summary": "Document not found",
                    "value": {
                      "statusCode": 404,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/status/doc_abc123",
                      "method": "GET",
                      "message": "Document not found"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Error retrieving document status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "statusError": {
                    "summary": "Error retrieving document status",
                    "value": {
                      "statusCode": 500,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/documents/status/doc_abc123",
                      "method": "GET",
                      "message": "Error retrieving document status"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "partner-access-token": []
          },
          {
            "partner-api-key": []
          }
        ],
        "summary": "Get processing status of a document",
        "tags": [
          "Documents"
        ]
      }
    },
    "/partner-api/account/returns-usage": {
      "get": {
        "description": "Returns usage counts and entitlement for the partner's active organization. lifecycleStage and trialReturnLimit semantics are documented on the 200 response schema. Requires X-Partner-Api-Key and Bearer access token.",
        "operationId": "PartnerAccountController_getReturnsUsage",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReturnsUsageResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing/Invalid API key or Unauthorized access token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "missingApiKey": {
                    "summary": "Missing API key",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/account/returns-usage",
                      "method": "GET",
                      "message": "Missing API key"
                    }
                  },
                  "invalidApiKey": {
                    "summary": "Invalid API key",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/account/returns-usage",
                      "method": "GET",
                      "message": "Invalid API key"
                    }
                  },
                  "unauthorized": {
                    "summary": "Unauthorized access token",
                    "value": {
                      "statusCode": 401,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/account/returns-usage",
                      "method": "GET",
                      "message": "Unauthorized"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No active organization found for this partner, or no subscription found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "noOrganization": {
                    "summary": "No active organization",
                    "value": {
                      "statusCode": 404,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/account/returns-usage",
                      "method": "GET",
                      "message": "No active organization found for this partner"
                    }
                  },
                  "noSubscription": {
                    "summary": "No subscription",
                    "value": {
                      "statusCode": 404,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/account/returns-usage",
                      "method": "GET",
                      "message": "No subscription found for organization"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Failed to retrieve returns usage information",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "fetchFailed": {
                    "summary": "Failed to retrieve returns usage",
                    "value": {
                      "statusCode": 500,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/account/returns-usage",
                      "method": "GET",
                      "message": "Failed to retrieve returns usage information"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "partner-access-token": []
          },
          {
            "partner-api-key": []
          }
        ],
        "summary": "Get returns usage and entitlement for your account",
        "tags": [
          "Account"
        ]
      }
    },
    "/partner-api/extraction/secure-link": {
      "post": {
        "description": "Step 1. Returns a presigned URL — PUT the file to signed_url, then pass url as s3Url in POST /partner-api/extraction/documents. HMAC-signed (service account).",
        "operationId": "ExtractionController_secureLink",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExtractionSecureLinkDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExtractionSecureLinkResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "validationFailed": {
                    "summary": "Request validation failed",
                    "value": {
                      "statusCode": 400,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/extraction/secure-link",
                      "method": "POST",
                      "message": [
                        "documentName should not be empty"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid HMAC signature"
          }
        },
        "security": [
          {
            "partner-signature": []
          }
        ],
        "summary": "Generate a pre-signed S3 URL for an extraction document upload",
        "tags": [
          "Extraction"
        ]
      }
    },
    "/partner-api/extraction/documents": {
      "post": {
        "description": "Step 2. Registers the S3 object and starts the extraction pipeline. See documentType for supported source document types. Idempotent on externalDocumentId. HMAC-signed (service account). Optionally supply client context (externalClientId, taxYear, clientFirstName, clientLastName — all four together) to group documents under one client and tax preparation per (externalClientId, taxYear); omit all four to keep the default per-document behavior. Supplying only some of the four returns 400.",
        "operationId": "ExtractionController_registerDocument",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterExtractionDocumentDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExtractionDocumentResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed or unsupported document type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "validationFailed": {
                    "summary": "Request validation failed",
                    "value": {
                      "statusCode": 400,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/extraction/documents",
                      "method": "POST",
                      "message": [
                        "s3Url should not be empty"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid HMAC signature"
          }
        },
        "security": [
          {
            "partner-signature": []
          }
        ],
        "summary": "Register an uploaded source document and start extraction",
        "tags": [
          "Extraction"
        ]
      }
    },
    "/partner-api/extraction/documents/{externalDocumentId}": {
      "get": {
        "description": "Returns the current processing state for a document you registered. Path parameter is your externalDocumentId. HMAC-signed (service account).",
        "operationId": "ExtractionController_getStatus",
        "parameters": [
          {
            "name": "externalDocumentId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExtractionStatusResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid HMAC signature"
          },
          "404": {
            "description": "Document not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerApiErrorResponseDto"
                },
                "examples": {
                  "notFound": {
                    "summary": "Document not found",
                    "value": {
                      "statusCode": 404,
                      "timestamp": "2026-06-08T12:00:00.000Z",
                      "path": "/partner-api/extraction/documents/doc_abc123",
                      "method": "GET",
                      "message": "Document not found"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "partner-signature": []
          }
        ],
        "summary": "Get the extraction status of a document",
        "tags": [
          "Extraction"
        ]
      }
    }
  },
  "info": {
    "title": "Juno Partner API",
    "description": "The Juno Partner API supports user-linked document workflows and service-account source-document extraction.\n\nEach endpoint documents its own request schema, response shapes, enum values, and error examples. All errors share a standard envelope: { statusCode, timestamp, path, method, message } — identify errors by HTTP status and message text.\n\nAuthentication:\n- User-linked Documents / Account — X-Partner-Api-Key + Authorization Bearer (JWT from onboarding)\n- Extraction (service account) — X-Partner-Api-Key + X-Partner-Timestamp + X-Partner-Signature (HMAC; no JWT)\n- Hosts: api.juno.tax for partner API calls; app.juno.tax for user browser onboarding redirects\n\nOutbound webhooks: Juno POSTs a JSON array of events to your subscribed URL, signed with X-Juno-Signature / X-Juno-Request-Timestamp. Processed-document results use type taxDocument.processed with a data object (externalDocumentId, documentStatus, outcome, extractedFields or resultsUrl). See the integration guide for signing, payload examples, and the extraction upload sequence.",
    "version": "1.0",
    "contact": {}
  },
  "tags": [],
  "servers": [
    {
      "url": "https://api-stg.juno.tax",
      "description": "Sandbox"
    },
    {
      "url": "https://api.juno.tax",
      "description": "Production"
    }
  ],
  "components": {
    "securitySchemes": {
      "partner-api-key": {
        "type": "apiKey",
        "in": "header",
        "name": "x-partner-api-key",
        "description": "Your partner API key"
      },
      "partner-access-token": {
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "type": "http",
        "description": "Partner access token (internal admin use)"
      },
      "partner-signature": {
        "type": "apiKey",
        "in": "header",
        "name": "x-partner-signature",
        "description": "HMAC request signing for extraction service accounts (no JWT).\n\nSend three headers on every request:\n- x-partner-api-key: your public key id (the partner apiKey)\n- x-partner-timestamp: current unix time in milliseconds (must be within ±5 minutes of server time)\n- x-partner-signature: base64( HMAC-SHA256( signingSecret, signingString ) )\n\nsigningString = METHOD + \"\\n\" + path + \"\\n\" + sha256(rawBody) + \"\\n\" + timestamp\n\nwhere path excludes the query string and rawBody is the exact bytes sent (empty for bodyless GETs). The shared signingSecret is never transmitted. The signature binds the method, path, body, and timestamp, so it cannot be replayed against a different request or after the freshness window. During a secret rotation both the current and previous secret are accepted."
      }
    },
    "schemas": {
      "PartnerTokenCallbackDto": {
        "type": "object",
        "properties": {
          "accessToken": {
            "type": "string",
            "description": "Partner access token (JWT). Handle server-side only; do not log.",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
          },
          "refreshToken": {
            "type": "string",
            "description": "Partner refresh token (JWT). Handle server-side only; do not log.",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
          }
        },
        "required": [
          "accessToken",
          "refreshToken"
        ]
      },
      "PartnerApiErrorResponseDto": {
        "type": "object",
        "properties": {
          "statusCode": {
            "type": "number",
            "description": "HTTP status code (matches the response status)",
            "example": 401
          },
          "timestamp": {
            "type": "string",
            "example": "2026-06-08T12:00:00.000Z"
          },
          "path": {
            "type": "string",
            "description": "Request path that produced the error",
            "example": "/partner-api/account/returns-usage"
          },
          "method": {
            "type": "string",
            "description": "HTTP method of the request",
            "example": "GET"
          },
          "message": {
            "description": "Human-readable error message (string) or validation failures (string array)",
            "oneOf": [
              {
                "type": "string",
                "example": "Unauthorized"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "example": [
                  "s3Url must be a string",
                  "clientId should not be empty"
                ]
              }
            ]
          },
          "stack": {
            "type": "string",
            "description": "Stack trace. Present only in non-production (NODE_ENV !== prd). Omitted in production."
          }
        },
        "description": "All partner API errors return exactly this flat JSON object at the top level — no nested error object, no errorCode field. Identify errors by HTTP status and the top-level `message` field (string for application errors, string array when request body validation fails). In non-production environments only, a `stack` string may be included for debugging.",
        "required": [
          "statusCode",
          "timestamp",
          "path",
          "method",
          "message"
        ]
      },
      "OnboardUserResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Juno internal user ID (UUID)",
            "example": "c3d4e5f6-a7b8-9012-cdef-123456789012"
          },
          "email": {
            "type": "string",
            "example": "jane@example.com"
          },
          "firstName": {
            "type": "object",
            "example": "Jane"
          },
          "lastName": {
            "type": "object",
            "example": "Smith"
          },
          "orgId": {
            "type": "object",
            "description": "Juno internal organization ID (UUID)",
            "example": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
          },
          "role": {
            "type": "string",
            "enum": [
              "ADMIN",
              "ADMINISTRATOR",
              "USER"
            ],
            "example": "USER"
          }
        },
        "required": [
          "id",
          "email",
          "role"
        ]
      },
      "PartnerUserLinkDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
          },
          "userId": {
            "type": "string",
            "description": "Juno internal user ID (UUID)",
            "example": "c3d4e5f6-a7b8-9012-cdef-123456789012"
          },
          "partnerId": {
            "type": "string",
            "description": "Juno partner ID (UUID)",
            "example": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
          },
          "externalId": {
            "type": "string",
            "description": "Your opaque user identifier",
            "example": "user_abc123"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "example": "2026-06-08T12:00:00.000Z"
          },
          "isActive": {
            "type": "boolean",
            "example": true
          }
        },
        "required": [
          "id",
          "userId",
          "partnerId",
          "externalId",
          "createdAt",
          "isActive"
        ]
      },
      "PartnerOrganizationLinkDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
          },
          "organizationId": {
            "type": "string",
            "description": "Juno internal organization ID (UUID)",
            "example": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
          },
          "partnerId": {
            "type": "string",
            "description": "Juno partner ID (UUID)",
            "example": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
          },
          "externalId": {
            "type": "string",
            "description": "Your opaque organization identifier",
            "example": "org_abc123"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "example": "2026-06-08T12:00:00.000Z"
          },
          "isActive": {
            "type": "boolean",
            "example": true
          }
        },
        "required": [
          "id",
          "organizationId",
          "partnerId",
          "externalId",
          "createdAt",
          "isActive"
        ]
      },
      "OnboardCurrentUserResponseDto": {
        "type": "object",
        "properties": {
          "status": {
            "type": "number",
            "description": "HTTP-style status indicator (always 200 on success)",
            "example": 200
          },
          "partnerUser": {
            "$ref": "#/components/schemas/PartnerUserLinkDto"
          },
          "partnerOrg": {
            "$ref": "#/components/schemas/PartnerOrganizationLinkDto"
          }
        },
        "required": [
          "status",
          "partnerUser",
          "partnerOrg"
        ]
      },
      "PartnerAuthenticatedUserDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "c3d4e5f6-a7b8-9012-cdef-123456789012"
          },
          "email": {
            "type": "string",
            "example": "jane@example.com"
          },
          "firstName": {
            "type": "object",
            "example": "Jane"
          },
          "lastName": {
            "type": "object",
            "example": "Smith"
          },
          "orgId": {
            "type": "object",
            "example": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
          },
          "role": {
            "type": "string",
            "enum": [
              "ADMIN",
              "ADMINISTRATOR",
              "USER"
            ],
            "example": "USER"
          }
        },
        "required": [
          "id",
          "email",
          "role"
        ]
      },
      "AuthenticatePartnerUserResponseDto": {
        "type": "object",
        "properties": {
          "accessToken": {
            "type": "string",
            "description": "Partner access token (JWT, ~60 min lifetime)",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
          },
          "refreshToken": {
            "type": "string",
            "description": "Partner refresh token (JWT, ~30 day lifetime)",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
          },
          "user": {
            "$ref": "#/components/schemas/PartnerAuthenticatedUserDto"
          }
        },
        "required": [
          "accessToken",
          "refreshToken",
          "user"
        ]
      },
      "OnboardUserDto": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "description": "The email address of the user",
            "example": "user@example.com"
          },
          "password": {
            "type": "string",
            "description": "The password for the user",
            "example": "StrongPassword123!",
            "minimum": 8
          },
          "firstName": {
            "type": "string",
            "description": "The first name of the user",
            "example": "John"
          },
          "lastName": {
            "type": "string",
            "description": "The last name of the user",
            "example": "Doe"
          },
          "organizationName": {
            "type": "string",
            "description": "The name of the organization",
            "example": "TechCorp Inc."
          },
          "organizationSize": {
            "type": "string",
            "description": "The size of the organization",
            "example": "50-100"
          },
          "emailConfirmed": {
            "type": "boolean",
            "description": "Whether the email is confirmed",
            "example": false
          },
          "returns": {
            "type": "number",
            "description": "The number of returns",
            "example": 10
          },
          "businessReturns": {
            "type": "number",
            "description": "The number of business returns",
            "example": 100
          },
          "taxSoftware": {
            "type": "string",
            "description": "Tax preparation software used by the firm.\n\n- LACERTE — Intuit Lacerte\n- PROCONNECT — Intuit ProConnect Tax Online\n- DRAKE — Drake Tax\n- CCHAXCESS — CCH Axcess Tax\n- ULTRATAX — Wolters Kluwer UltraTax\n- OTHER — Other tax software",
            "example": "LACERTE"
          },
          "practiceManagementSoftware": {
            "type": "string",
            "description": "Practice management software used by the firm.\n\n- TAXDOME — TaxDome\n- CANOPY — Canopy\n- KARBON — Karbon\n- FINANCIAL_CENTS — Financial Cents\n- CCH — CCH\n- QUONT — Quont\n- FIRM_360 — Firm360\n- PRACTICE_CS — Practice CS\n- OTHER — Other practice management software",
            "example": "TAXDOME"
          },
          "externalUserId": {
            "type": "string",
            "description": "Your opaque user identifier. Must be stable for the same user across API calls.",
            "example": "user_abc123"
          },
          "externalOrgId": {
            "type": "string",
            "description": "Your opaque organization identifier. Must be stable for the same org across API calls.",
            "example": "org_abc123"
          },
          "partnerId": {
            "type": "string",
            "description": "Juno partner ID (UUID) assigned to your integration",
            "example": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
          }
        },
        "required": [
          "email",
          "password",
          "firstName",
          "lastName",
          "organizationName",
          "organizationSize",
          "externalUserId",
          "externalOrgId",
          "partnerId"
        ]
      },
      "OnboardCurrentUserDto": {
        "type": "object",
        "properties": {
          "externalUserId": {
            "type": "string",
            "description": "Your opaque user identifier. Must match the ID used during pre-onboard.",
            "example": "user_abc123"
          },
          "externalOrgId": {
            "type": "string",
            "description": "Your opaque organization identifier. Must match the ID used during pre-onboard.",
            "example": "org_abc123"
          },
          "partnerId": {
            "type": "string",
            "description": "Juno partner ID (UUID) assigned to your integration",
            "example": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
          },
          "orgId": {
            "type": "string",
            "description": "Juno internal organization ID (UUID)",
            "example": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
          },
          "userId": {
            "type": "string",
            "description": "Juno internal user ID (UUID)",
            "example": "c3d4e5f6-a7b8-9012-cdef-123456789012"
          }
        },
        "required": [
          "externalUserId",
          "externalOrgId",
          "partnerId",
          "orgId",
          "userId"
        ]
      },
      "PrepareOnboardUserDTO": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "description": "Your internal user ID",
            "example": "user_abc123"
          },
          "orgId": {
            "type": "string",
            "description": "Your internal org ID",
            "example": "org_abc123"
          },
          "callbackUrl": {
            "type": "string",
            "description": "HTTPS URL on a whitelisted origin where Juno redirects the user after onboarding completes. Tokens are currently appended as query parameters (see security notice).\n\nKnown limitation — token callback via query parameters:\nAfter onboarding, Juno redirects the user's browser to your callbackUrl with accessToken and refreshToken as URL query parameters. Tokens in URLs can be exposed via browser history, referrer headers, proxy logs, and server access logs.\n\nPlanned remediation (future):\nJuno will replace query-parameter token delivery with a short-lived, one-time authorization code that partners exchange server-to-server over HTTPS. Partners will no longer receive JWTs in callback URLs. Juno will provide a migration guide before the change ships.\n\nInterim mitigations (required until migration):\n- Register callback URLs on HTTPS origins only; Juno validates against your whitelisted callback origins\n- Handle the callback on your backend — never persist tokens in client-side storage from the redirect\n- Extract tokens immediately, then redirect the user to a clean URL with query parameters stripped\n- Never log callback URLs, query strings, or token values\n- Store tokens server-side only and use POST /partner-api/auth/refresh for renewal",
            "example": "https://yourapp.com/callback"
          },
          "firstName": {
            "type": "string",
            "example": "Jane"
          },
          "lastName": {
            "type": "string",
            "example": "Smith"
          },
          "email": {
            "type": "string",
            "example": "jane@example.com"
          },
          "companyName": {
            "type": "string",
            "example": "Acme Tax LLC"
          },
          "practiceManagementSoftware": {
            "type": "string",
            "description": "Practice management software used by the firm.\n\n- TAXDOME — TaxDome\n- CANOPY — Canopy",
            "example": "TAXDOME"
          }
        },
        "required": [
          "userId",
          "orgId",
          "callbackUrl"
        ]
      },
      "PrepareOnboardResponseDto": {
        "type": "object",
        "properties": {
          "redirectUrl": {
            "type": "string",
            "description": "Onboarding URL to redirect the user to",
            "example": "https://app.juno.tax/onboarding/partner?orgId=org_abc123&userId=user_abc123&withCallbackUrl=https%3A%2F%2Fyourapp.com%2Fcallback&partnerId=a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
          }
        },
        "required": [
          "redirectUrl"
        ]
      },
      "PartnerRefreshTokenResponseDto": {
        "type": "object",
        "properties": {
          "accessToken": {
            "type": "string",
            "description": "New access token (JWT, ~60 min lifetime). Use for subsequent API calls.",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
          },
          "refreshToken": {
            "type": "string",
            "description": "New refresh token (JWT, ~30 day lifetime). Replaces the previous refresh token — persist immediately.",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
          }
        },
        "required": [
          "accessToken",
          "refreshToken"
        ]
      },
      "AuthenticatePartnerUserDto": {
        "type": "object",
        "properties": {
          "externalUserId": {
            "type": "string",
            "description": "Your opaque user identifier",
            "example": "user_abc123"
          },
          "externalOrgId": {
            "type": "string",
            "description": "Your opaque organization identifier",
            "example": "org_abc123"
          },
          "partnerId": {
            "type": "string",
            "description": "Juno partner ID (UUID) assigned to your integration",
            "example": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
          }
        },
        "required": [
          "externalUserId",
          "externalOrgId",
          "partnerId"
        ]
      },
      "GenerateUploadUrlDto": {
        "type": "object",
        "properties": {
          "documentId": {
            "type": "string",
            "description": "Your unique document ID (must match the ID used in POST /documents/upload)",
            "example": "doc_abc123"
          },
          "documentName": {
            "type": "string",
            "description": "File name including extension. Extension is used for processing dispatch if mimeType is omitted.",
            "example": "w2-2025.pdf"
          },
          "mimeType": {
            "type": "string",
            "enum": [
              "application/pdf",
              "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
              "application/vnd.ms-excel",
              "text/csv",
              "image/png",
              "image/jpeg"
            ],
            "description": "MIME type of the file. Defaults to application/pdf if omitted.\n\n- application/pdf — pdf\n- application/vnd.openxmlformats-officedocument.spreadsheetml.sheet — xlsx\n- application/vnd.ms-excel — xls\n- text/csv — csv\n- image/png — png\n- image/jpeg — jpg, jpeg",
            "example": "application/pdf"
          }
        },
        "required": [
          "documentId",
          "documentName"
        ]
      },
      "GeneratePresignedUrlResponseDto": {
        "type": "object",
        "properties": {
          "signed_url": {
            "type": "string",
            "description": "HTTPS presigned URL for uploading the file (PUT request)",
            "example": "https://bucket.s3.amazonaws.com/env/uuid_w2-2025.pdf?X-Amz-..."
          },
          "url": {
            "type": "string",
            "description": "S3 URI to pass as s3Url in POST /documents/upload after upload completes",
            "example": "s3://bucket-name/env/uuid_w2-2025.pdf"
          }
        },
        "required": [
          "signed_url",
          "url"
        ]
      },
      "UploadDocumentDto": {
        "type": "object",
        "properties": {
          "s3Url": {
            "type": "string",
            "description": "S3 URL returned from POST /documents/secure-link (the url field, after uploading to signed_url)",
            "example": "s3://bucket-name/env/uuid_w2-2025.pdf"
          },
          "documentType": {
            "type": "string",
            "enum": [
              "SOURCE",
              "RETURN",
              "BROKERAGE",
              "PNL",
              "BALANCE_SHEET"
            ],
            "description": "Partner-facing document type. Defaults to SOURCE.\n\n- SOURCE — Client source documents (W-2, 1099, etc.). Default for individual (1040) uploads.\n- RETURN — Prior-year return PDF. Maps to the preparation form type (1065/1120/1120S).\n- BROKERAGE — Brokerage statements.\n- PNL — Profit & loss statement (business returns).\n- BALANCE_SHEET — Balance sheet (business returns).\n\nPNL and BALANCE_SHEET require a business preparationType (not FORM_1040).",
            "example": "SOURCE"
          },
          "clientId": {
            "type": "string",
            "description": "Your internal client ID. Must be consistent across uploads for the same client.",
            "example": "client_abc123"
          },
          "taxYear": {
            "type": "number",
            "description": "Calendar tax year for the preparation. Integer (e.g. 2025). Values above 2025 are capped to 2025. Use the filing year for the return being prepared.",
            "example": 2025
          },
          "preparationType": {
            "type": "string",
            "enum": [
              "FORM_1040",
              "FORM_1065",
              "FORM_1120",
              "FORM_1120S"
            ],
            "description": "Tax return form type. Defaults to FORM_1040.\n\n- FORM_1040 — Individual 1040. Required: clientFirstName, clientLastName.\n- FORM_1065 — Partnership 1065. Required: businessName. Optional: taxIdentifier (EIN).\n- FORM_1120 — C-Corp 1120. Required: businessName. Optional: taxIdentifier (EIN).\n- FORM_1120S — S-Corp 1120S. Required: businessName. Optional: taxIdentifier (EIN).",
            "example": "FORM_1040"
          },
          "businessName": {
            "type": "string",
            "description": "Required when preparationType is FORM_1065, FORM_1120, or FORM_1120S",
            "example": "Acme Corp"
          },
          "taxIdentifier": {
            "type": "string",
            "description": "EIN for business returns (e.g. \"12-3456789\"). Ignored for FORM_1040.",
            "example": "12-3456789"
          },
          "clientFirstName": {
            "type": "string",
            "description": "Required when preparationType is absent or FORM_1040",
            "example": "Jane"
          },
          "clientLastName": {
            "type": "string",
            "description": "Required when preparationType is absent or FORM_1040",
            "example": "Smith"
          },
          "documentId": {
            "type": "string",
            "description": "Your unique document ID (used for status polling)",
            "example": "doc_abc123"
          }
        },
        "required": [
          "s3Url",
          "clientId",
          "taxYear",
          "documentId"
        ]
      },
      "UploadDocumentResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Juno internal partner document ID",
            "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
          },
          "externalDocumentId": {
            "type": "string",
            "description": "Your documentId from the upload request",
            "example": "doc_abc123"
          },
          "taxDocumentId": {
            "type": "string",
            "description": "Juno internal tax document ID",
            "example": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
          },
          "status": {
            "type": "string",
            "enum": [
              "UPLOADED",
              "QUEUED",
              "PRE_PROCESSING",
              "CLASSIFICATION",
              "OCR",
              "PROCESSING",
              "PROCESSED",
              "ERROR",
              "ERROR_OCR",
              "ERROR_POST_PROCESSING",
              "ERROR_CLASSIFICATION",
              "ERROR_BAD_INPUT_DATA",
              "ERROR_NOT_RECENT_1040",
              "ERROR_UNSUPPORTED_FORM",
              "ARCHIVED"
            ],
            "description": "Initial processing status on this upload response. Field name is `status` (not documentStatus — that name is used on GET /documents/status/:id).\n\nProcessing state of the document.\n\nIn-progress (keep polling): UPLOADED, QUEUED, PRE_PROCESSING, CLASSIFICATION, OCR, PROCESSING\n\nTerminal — success (stop polling): PROCESSED\n\nTerminal — failure (stop polling): ERROR, ERROR_OCR, ERROR_POST_PROCESSING, ERROR_CLASSIFICATION, ERROR_BAD_INPUT_DATA, ERROR_NOT_RECENT_1040, ERROR_UNSUPPORTED_FORM\n\nTerminal — other (stop polling): ARCHIVED\n\nNote: documents split into child documents are internally archived but returned as PROCESSED.",
            "example": "UPLOADED"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "ISO 8601 timestamp when the partner document record was created",
            "example": "2026-06-08T12:00:00.000Z"
          }
        },
        "required": [
          "id",
          "externalDocumentId",
          "taxDocumentId",
          "status",
          "createdAt"
        ]
      },
      "DocumentStatusResponseDto": {
        "type": "object",
        "properties": {
          "documentStatus": {
            "type": "string",
            "enum": [
              "UPLOADED",
              "QUEUED",
              "PRE_PROCESSING",
              "CLASSIFICATION",
              "OCR",
              "PROCESSING",
              "PROCESSED",
              "ERROR",
              "ERROR_OCR",
              "ERROR_POST_PROCESSING",
              "ERROR_CLASSIFICATION",
              "ERROR_BAD_INPUT_DATA",
              "ERROR_NOT_RECENT_1040",
              "ERROR_UNSUPPORTED_FORM",
              "ARCHIVED"
            ],
            "description": "Processing state for this document. Field name is `documentStatus` (not status — upload responses use `status`).\n\nProcessing state of the document.\n\nIn-progress (keep polling): UPLOADED, QUEUED, PRE_PROCESSING, CLASSIFICATION, OCR, PROCESSING\n\nTerminal — success (stop polling): PROCESSED\n\nTerminal — failure (stop polling): ERROR, ERROR_OCR, ERROR_POST_PROCESSING, ERROR_CLASSIFICATION, ERROR_BAD_INPUT_DATA, ERROR_NOT_RECENT_1040, ERROR_UNSUPPORTED_FORM\n\nTerminal — other (stop polling): ARCHIVED\n\nNote: documents split into child documents are internally archived but returned as PROCESSED.",
            "example": "PROCESSING"
          }
        },
        "required": [
          "documentStatus"
        ]
      },
      "ReturnsUsageResponseDto": {
        "type": "object",
        "properties": {
          "numReturnsUsed": {
            "type": "number",
            "description": "Number of returns consumed in the current billing period (or trial period for trial accounts)",
            "example": 5
          },
          "numReturnsAvailable": {
            "type": "number",
            "description": "Returns remaining before hitting the purchase or trial limit (numReturnsPurchased or trialReturnLimit minus numReturnsUsed)",
            "example": 45
          },
          "numReturnsPurchased": {
            "type": "number",
            "description": "Total returns purchased for the subscription. Zero for trial accounts (isTrialAccount = true).",
            "example": 50
          },
          "isTrialAccount": {
            "type": "boolean",
            "description": "True for trial accounts — trialReturnLimit applies instead of numReturnsPurchased",
            "example": false
          },
          "trialReturnLimit": {
            "type": "number",
            "description": "Maximum returns allowed during trial. Omitted when isTrialAccount is false."
          },
          "lifecycleStage": {
            "type": "string",
            "description": "Subscription lifecycle stage. Use isTrialAccount to determine whether trial or purchase limits apply.",
            "example": "customer"
          }
        },
        "required": [
          "numReturnsUsed",
          "numReturnsAvailable",
          "numReturnsPurchased",
          "isTrialAccount",
          "lifecycleStage"
        ]
      },
      "ExtractionSecureLinkDto": {
        "type": "object",
        "properties": {
          "documentId": {
            "type": "string",
            "description": "Your unique document ID (must match externalDocumentId used in POST /partner-api/extraction/documents)",
            "example": "doc_abc123"
          },
          "documentName": {
            "type": "string",
            "description": "File name including extension. Extension is used for processing dispatch if mimeType is omitted.",
            "example": "source-document-2025.pdf"
          },
          "mimeType": {
            "type": "string",
            "enum": [
              "application/pdf",
              "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
              "application/vnd.ms-excel",
              "text/csv",
              "image/png",
              "image/jpeg"
            ],
            "description": "MIME type of the file. Defaults to application/pdf if omitted.\n\n- application/pdf — pdf\n- application/vnd.openxmlformats-officedocument.spreadsheetml.sheet — xlsx\n- application/vnd.ms-excel — xls\n- text/csv — csv\n- image/png — png\n- image/jpeg — jpg, jpeg",
            "example": "application/pdf"
          }
        },
        "required": [
          "documentId",
          "documentName"
        ]
      },
      "ExtractionSecureLinkResponseDto": {
        "type": "object",
        "properties": {
          "signed_url": {
            "type": "string",
            "description": "HTTPS presigned URL for uploading the file (PUT request)",
            "example": "https://bucket.s3.amazonaws.com/env/uuid_source-document-2025.pdf?X-Amz-..."
          },
          "url": {
            "type": "string",
            "description": "S3 URI to pass as s3Url in POST /partner-api/extraction/documents after upload completes",
            "example": "s3://bucket-name/env/uuid_source-document-2025.pdf"
          }
        },
        "required": [
          "signed_url",
          "url"
        ]
      },
      "RegisterExtractionDocumentDto": {
        "type": "object",
        "properties": {
          "externalDocumentId": {
            "type": "string",
            "description": "Your unique document ID. Used for status polling and to correlate the webhook result. Idempotent: re-registering the same id returns the original document.",
            "example": "doc_abc123"
          },
          "s3Url": {
            "type": "string",
            "description": "S3 URL returned from POST /partner-api/extraction/secure-link (the url field, after uploading to signed_url)",
            "example": "s3://bucket-name/env/uuid_source-document-2025.pdf"
          },
          "documentType": {
            "type": "string",
            "enum": [
              "BROKERAGE",
              "FORM_1099"
            ],
            "description": "The source document type. BROKERAGE for consolidated brokerage statements (e.g. 1099-B); FORM_1099 for standalone 1099-INT/DIV/MISC information returns (the engine determines the exact form).",
            "example": "BROKERAGE"
          },
          "callbackUrl": {
            "type": "string",
            "description": "Optional URL recorded with the document for your own correlation. Result delivery uses your configured webhook subscription, not this field.",
            "example": "https://example.com/callbacks/doc_abc123"
          },
          "externalClientId": {
            "type": "string",
            "description": "Your stable client identifier. When supplied together with taxYear, clientFirstName and clientLastName, documents sharing the same (externalClientId, taxYear) are grouped under one client and tax preparation. Omit all four client fields to keep the default per-document extraction behavior.",
            "example": "client_abc123"
          },
          "taxYear": {
            "type": "integer",
            "description": "Tax year for the preparation this document belongs to. Required only when supplying client context (with externalClientId, clientFirstName, clientLastName).",
            "example": 2025
          },
          "clientFirstName": {
            "type": "string",
            "description": "Client first name. Required only when supplying client context. Used to populate the client record (the clients identity key is externalClientId).",
            "example": "Jane"
          },
          "clientLastName": {
            "type": "string",
            "description": "Client last name. Required only when supplying client context.",
            "example": "Doe"
          }
        },
        "required": [
          "externalDocumentId",
          "s3Url",
          "documentType"
        ]
      },
      "ExtractionDocumentResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Juno internal partner document ID",
            "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
          },
          "externalDocumentId": {
            "type": "string",
            "description": "Your externalDocumentId from the register request",
            "example": "doc_abc123"
          },
          "taxDocumentId": {
            "type": "string",
            "description": "Juno internal tax document ID",
            "example": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
          },
          "status": {
            "type": "string",
            "enum": [
              "UPLOADED",
              "QUEUED",
              "PRE_PROCESSING",
              "CLASSIFICATION",
              "OCR",
              "PROCESSING",
              "PROCESSED",
              "ERROR",
              "ERROR_OCR",
              "ERROR_POST_PROCESSING",
              "ERROR_CLASSIFICATION",
              "ERROR_BAD_INPUT_DATA",
              "ERROR_NOT_RECENT_1040",
              "ERROR_UNSUPPORTED_FORM",
              "ARCHIVED"
            ],
            "description": "Initial processing status on this response.\n\nProcessing state of the document.\n\nIn-progress (keep polling): UPLOADED, QUEUED, PRE_PROCESSING, CLASSIFICATION, OCR, PROCESSING\n\nTerminal — success (stop polling): PROCESSED\n\nTerminal — failure (stop polling): ERROR, ERROR_OCR, ERROR_POST_PROCESSING, ERROR_CLASSIFICATION, ERROR_BAD_INPUT_DATA, ERROR_NOT_RECENT_1040, ERROR_UNSUPPORTED_FORM\n\nTerminal — other (stop polling): ARCHIVED\n\nNote: documents split into child documents are internally archived but returned as PROCESSED.",
            "example": "UPLOADED"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "ISO 8601 timestamp when the document record was created",
            "example": "2026-06-24T12:00:00.000Z"
          }
        },
        "required": [
          "id",
          "externalDocumentId",
          "taxDocumentId",
          "status",
          "createdAt"
        ]
      },
      "ExtractionStatusResponseDto": {
        "type": "object",
        "properties": {
          "documentStatus": {
            "type": "string",
            "enum": [
              "UPLOADED",
              "QUEUED",
              "PRE_PROCESSING",
              "CLASSIFICATION",
              "OCR",
              "PROCESSING",
              "PROCESSED",
              "ERROR",
              "ERROR_OCR",
              "ERROR_POST_PROCESSING",
              "ERROR_CLASSIFICATION",
              "ERROR_BAD_INPUT_DATA",
              "ERROR_NOT_RECENT_1040",
              "ERROR_UNSUPPORTED_FORM",
              "ARCHIVED"
            ],
            "description": "Processing state for this document.\n\nProcessing state of the document.\n\nIn-progress (keep polling): UPLOADED, QUEUED, PRE_PROCESSING, CLASSIFICATION, OCR, PROCESSING\n\nTerminal — success (stop polling): PROCESSED\n\nTerminal — failure (stop polling): ERROR, ERROR_OCR, ERROR_POST_PROCESSING, ERROR_CLASSIFICATION, ERROR_BAD_INPUT_DATA, ERROR_NOT_RECENT_1040, ERROR_UNSUPPORTED_FORM\n\nTerminal — other (stop polling): ARCHIVED\n\nNote: documents split into child documents are internally archived but returned as PROCESSED.",
            "example": "PROCESSING"
          }
        },
        "required": [
          "documentStatus"
        ]
      }
    }
  }
}
