Start with the reason this software should exist, not a list of screens. Which people will use it day to day, how often, and what happens today? An experienced team who grasps the purpose often proposes an alternative that costs less; someone handed only a feature list will price exactly what you asked for.
Describe the scope as concrete flows: what the user does and what the system does in response. Just as important, list what is out of scope. An explicit exclusion list removes more friction later than almost anything else in the document. Indicate as well which parts are firm and which may still change — estimators price uncertainty, and pretending everything is fixed helps nobody.
Write down the hard constraints. This means the platforms and typescript development services involved, existing databases and their quality, compliance requirements, laravel vs nodejs expected load, target platforms and any technology you are committed to. Where a date is genuinely fixed, say what depends on it: a team is usually able to rearrange the plan to hit it, but not if the date is a secret.
Say what done means for the important items. Testable acceptance criteria do not need any formal notation: a short paragraph describing what a user should be able to do will do. This single habit compresses acceptance testing dramatically and closes off the usual argument at handover.
Finally, say what you expect back. Ask for a breakdown by feature or module, the assumptions behind each number, the risks the team sees and an optimistic and a pessimistic figure. Take a broad range as a signal about the brief: it usually points to where your description is thin. Then tighten that section and request a revised number — the next version is much more reliable.