Describe the scope as concrete flows: who does what, and what happens next. Every bit as useful, write down what you are not building. An explicit exclusion list saves more argument later than almost anything else in the document.
Define what completion means for the important items. Clear acceptance criteria do not need special syntax: a short list stating what a user should be able to do is sufficient.
Write down the hard constraints. This means the platforms and services involved, the data you already hold and its condition, security and compliance rules, traffic expectations, target platforms and stacks you cannot change.
Be wary of a gap between the people you meet and the developers actually assigned. Insist on the names and CVs of the actual team in the agreement, with wording covering replacement.
Vague phrasing around IP is rarely an oversight. The document should state plainly that all outputs produced under it become the property of the client as they are paid for.
Connections to other systems tend to be another reliable source of cost. A feature that touches only your own data is easy to estimate; the same feature wired into a legacy ERP is not.
Last, pay attention to how they communicate. Confirm how many hours there will be with your timezone, who handles your questions and within what time.
Require the source repository from day one. A partner that hands over nothing between demos is asking you to take delivery on faith. Daily commits reveal the actual pace far better than a weekly report.