curl can attach a request body in several ways, and they are not interchangeable.
-d / --data sends the value and, importantly, sets Content-Type: application/x-www-form-urlencoded by default. This is the most common curl surprise: curl -d '{"name":"Alice"}' sends JSON text but labels it as a form, so a JSON API may reject it. The fix is to state the type: add -H "Content-Type: application/json".
--data-urlencode percent-encodes its value, which is what you want for form fields that contain spaces or symbols. --data-binary sends the bytes exactly, without the newline stripping that -d performs. Multiple -d flags are joined with &.
-F / --form builds a multipart/form-data body. Each -F name=value is a field, and -F name=@file attaches a file. curl chooses the multipart boundary for you, so you never set it by hand.
When you translate a curl command to another language, carry the Content-Type curl actually sends, not the one you would guess from the body's shape. That single detail is where most hand conversions go wrong.