Laravel post file with HTTP::attach error : A 'contents' key is required

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

Laravel HTTP::attach Error: Mastering File Uploads with the Laravel Client

As senior developers working with modern PHP frameworks like Laravel, we frequently interact with external APIs and services using the Illuminate\Support\Facades\Http client. When dealing with file uploads—attaching actual files to an HTTP request—things can sometimes get surprisingly tricky.

Recently, I encountered a common stumbling block related to sending file data via Http::attach(), resulting in the frustrating error: "A 'contents' key is required." This post dives deep into why this happens and provides the correct, robust way to handle file attachments in Laravel.


Understanding the HTTP Client and File Attachments

The core issue lies in how the Laravel HTTP client constructs the underlying multipart/form-data request when using methods like attach(). While attaching a simple file stream is straightforward, sending complex data alongside that file requires careful structuring of the payload.

When you use Http::attach('filename', $contents, $filename), you are telling the client to upload a file. However, if your target endpoint expects specific fields (like name or contents) within the form data rather than just raw file streams, you need to structure the request body correctly.

The documentation for the Laravel HTTP Client provides excellent guidance on this, emphasizing that attachments handle the binary data, but accompanying metadata must be explicitly defined in the request payload.

Why Your Attempts Failed

Let's analyze the attempts you made:

Attempt 1 (Failing):

$response = Http::attach('pdfFile',$stage->pdfFile,$stage->n_stage.'.pdf')
    ->post($this->endpointFileUploadUrl);

This structure correctly attaches the file stream, but it doesn't provide any explicit form data for the server to process alongside the file. It only sends the file itself.

Attempt 2 (Failing):

$response = Http::attach('pdfFile',$stage->pdfFile,$stage->n_stage.'.pdf')
    ->post($this->endpointFileUploadUrl, [
        'name' => 'pdfFile',
        'contents' => $stage->pdfFile,
    ]);

You correctly recognized that you needed to pass an array of data to the post() method. However, the error "A 'contents' key is required" indicates that even when using the array syntax for POST data, the specific endpoint you are targeting requires a particular structure where the file content must be explicitly mapped under the key contents.

The Correct Implementation: Structuring Multipart Data

The solution is to combine the attachment of the file (using attach()) with the explicit definition of the form fields (using an array for the body payload). You need to ensure that both the file and its metadata are sent together in a cohesive multipart request.

Here is the recommended approach, ensuring all necessary data points are correctly transmitted:

public function sendPdfFile($id)
{
    try {
        $stage = Stage::findOrFail($id);
        $stage->setPdfFileAttribute();

        // 1. Prepare the file content and filename
        $fileName = $stage->n_stage . '.pdf';
        $fileContents = $stage->pdfFile;

        // 2. Send the request, attaching the file AND defining the required payload data simultaneously.
        $response = Http::attach(
            'pdfFile', // The field name the server expects for the file part
            $fileContents, // The actual binary content of the file
            $fileName      // The filename to use on the server side
        )
        ->withBody([
            'name' => 'pdfFile',       // Explicitly defining the 'name' field
            'contents' => $fileContents, // Providing the required 'contents' key with the data
        ])
        ->post($this->endpointFileUploadUrl);

        // Handle the response...
        $response->throw(); 

    } catch (\Exception $exception) {
        $this->error("Error sending file {$stage->n_stage}.pdf to {$this->endpointFileUploadUrl}: " . $exception->getMessage());
    }
}

Key Takeaways and Best Practices

  1. Separation of Concerns: Use attach() strictly for providing the raw file stream. Use the subsequent array structure within withBody() (or directly in the post call, depending on the client version/context) to define the specific form fields (name, contents) that your API endpoint is expecting.
  2. Documentation is King: Always refer back to official documentation when debugging HTTP interactions. For further reading on robust API interaction patterns, I highly recommend exploring resources from https://laravelcompany.com.
  3. Test the Endpoint: If you continue to face issues, verify the exact expected structure of your target endpoint. Ensure that the server expects multipart/form-data and is set up to parse both file parts and standard form fields correctly.

By structuring your request this way, you ensure that the file data is attached correctly while simultaneously satisfying the specific requirements of the receiving API by supplying the required 'contents' key. Happy coding!