Link Receipt or Invoice
Creates an attachment record linked to a transaction and returns a presigned S3 URL for direct file upload.
2-step flow:
- Call this endpoint with file metadata and the transaction ID — returns the attachment
idand theuploadUrl. - PUT the file binary to
uploadUrldirectly to S3 (not through the API), sending theContent-Typeheader for the file format.
The returned id can be used in Download attachment once the upload completes.
File format: file.name must end with an extension accepted for file.contentType, case-insensitive. If it doesn't, the response is 400. On the PUT, send the Content-Type listed for the format; with any other value, S3 rejects the upload with 403 (SignatureDoesNotMatch).
PDF: extension.pdf,Content-Type: application/pdfPNG: extension.png,Content-Type: image/pngJPEGorJPG: extension.jpegor.jpg,Content-Type: image/jpegXLS: extension.xls,Content-Type: application/vnd.ms-excelXLSX: extension.xlsx,Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheetODF: extension.odfor.odt,Content-Type: application/vnd.oasis.opendocument.textOFX: extension.ofx,Content-Type: application/x-ofx
Idempotency: this endpoint does not support the Idempotency-Key header. Each call creates a new attachment record — a transaction can have multiple linked attachments.
curl -X POST "https://api-sandbox.contasimples.com/attachments/v1/attachments" \
-H "Content-Type: application/json" \
-H "Signature-Input: example_string" \
-H "Signature: example_string" \
-H "Content-Digest: example_string" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"transactionId": "111304",
"file": {
"name": "invoice.pdf",
"contentType": "PDF"
},
"description": "Invoice for the purchase"
}'
import requests
import json
url = "https://api-sandbox.contasimples.com/attachments/v1/attachments"
headers = {
"Content-Type": "application/json",
"Signature-Input": "example_string",
"Signature": "example_string",
"Content-Digest": "example_string",
"Authorization": "Bearer YOUR_API_TOKEN"
}
data = {
"transactionId": "111304",
"file": {
"name": "invoice.pdf",
"contentType": "PDF"
},
"description": "Invoice for the purchase"
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const response = await fetch("https://api-sandbox.contasimples.com/attachments/v1/attachments", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Signature-Input": "example_string",
"Signature": "example_string",
"Content-Digest": "example_string",
"Authorization": "Bearer YOUR_API_TOKEN"
},
body: JSON.stringify({
"transactionId": "111304",
"file": {
"name": "invoice.pdf",
"contentType": "PDF"
},
"description": "Invoice for the purchase"
})
});
const data = await response.json();
console.log(data);
package main
import (
"fmt"
"net/http"
"bytes"
"encoding/json"
)
func main() {
data := []byte(`{
"transactionId": "111304",
"file": {
"name": "invoice.pdf",
"contentType": "PDF"
},
"description": "Invoice for the purchase"
}`)
req, err := http.NewRequest("POST", "https://api-sandbox.contasimples.com/attachments/v1/attachments", bytes.NewBuffer(data))
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Signature-Input", "example_string")
req.Header.Set("Signature", "example_string")
req.Header.Set("Content-Digest", "example_string")
req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
fmt.Println("Response Status:", resp.Status)
}
require 'net/http'
require 'json'
uri = URI('https://api-sandbox.contasimples.com/attachments/v1/attachments')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/json'
request['Signature-Input'] = 'example_string'
request['Signature'] = 'example_string'
request['Content-Digest'] = 'example_string'
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request.body = '{
"transactionId": "111304",
"file": {
"name": "invoice.pdf",
"contentType": "PDF"
},
"description": "Invoice for the purchase"
}'
response = http.request(request)
puts response.body
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "PROCESSING",
"referenceId": "111304",
"file": {
"name": "invoice.pdf",
"contentType": "PDF"
},
"description": "Invoice for purchase",
"createdAt": "2026-06-25T22:54:52.000Z",
"uploadUrl": "https://s3.amazonaws.com/bucket/key?X-Amz-Signature=..."
}
{
"error": "Bad Request",
"message": "One or more request parameters are invalid.",
"code": 400,
"requestId": "123e4567-e89b-12d3-a456-426614174000",
"details": [
"The example field must have a value between 5 and 100.",
"Dates must be in YYYY-MM-DD format."
]
}
{
"error": "Unauthorized",
"message": "Authentication failed. Check the API token and the request signature",
"requestId": "123e4567-e89b-12d3-a456-426614174000",
"code": 401
}
{
"error": "Forbidden",
"message": "The request is not allowed for the current credentials",
"code": 403,
"details": {
"requiredScope": "listCategories"
},
"requestId": "123e4567-e89b-12d3-a456-426614174000"
}
{
"error": "Internal Server Error",
"message": "An unexpected error occurred while processing the request.",
"requestId": "123e4567-e89b-12d3-a456-426614174000",
"code": 500
}
/attachments/v1/attachments
Target server for requests. Edit to use your own host.
Bearer token from OAuth 2.0 client credentials. Format: Bearer TOKEN
Bearer TOKENThe media type of the request body
What was signed and the signature parameters, in the RFC 9421 format. A credential with a public key must send it on every call, reads included, or the request is refused with 401. The signature covers the @method, @path, and @query components, and also content-digest when there is a body. created and alg are required, and created is valid for 300 seconds. The full format, with examples, is in Request Signing.
The signature of the base defined by RFC 9421, in base64, between : and under the same label used in Signature-Input, for example sig=:SGVsbG8gd29ybGQ=:. It always goes together with Signature-Input.
Hash of the request body in the RFC 9530 format, with sha-256 or sha-512, for example sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:. It is required when the request has a body and the credential has a public key, and in that case Signature-Input must include the content-digest component.
ID of the transaction to link the document to. Corresponds to the id field returned by Banking statement (numeric — send its value as a string) or by Credit card statement (ULID).
Document description (optional).
Request Preview
Response
Response will appear here after sending the request
Authentication
Bearer token. Bearer token from OAuth 2.0 client credentials. Format: Bearer TOKEN
Headers
What was signed and the signature parameters, in the RFC 9421 format. A credential with a public key must send it on every call, reads included, or the request is refused with 401. The signature covers the @method, @path, and @query components, and also content-digest when there is a body. created and alg are required, and created is valid for 300 seconds. The full format, with examples, is in Request Signing.
The signature of the base defined by RFC 9421, in base64, between : and under the same label used in Signature-Input, for example sig=:SGVsbG8gd29ybGQ=:. It always goes together with Signature-Input.
Hash of the request body in the RFC 9530 format, with sha-256 or sha-512, for example sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:. It is required when the request has a body and the credential has a public key, and in that case Signature-Input must include the content-digest component.
Body
ID of the transaction to link the document to. Corresponds to the id field returned by Banking statement (numeric — send its value as a string) or by Credit card statement (ULID).
111304