This guide demonstrates how to upload files to Blue using Python and the requests
library, avoiding the complexity of WebKit boundaries or curl formatting issues. It covers two scenarios:
requests
library installed: pip install requests
Define these constants at the top of your script:
FILENAME = "test.jpg"
TOKEN_ID = "YOUR_TOKEN_ID"
TOKEN_SECRET = "YOUR_TOKEN_SECRET"
COMPANY_ID = "YOUR_COMPANY_ID_OR_SLUG"
PROJECT_ID = "YOUR_PROJECT_ID_OR_SLUG"
BASE_URL = "https://api.blue.cc"
import requests
import json
import os
# Configuration
FILENAME = "test.jpg"
TOKEN_ID = "YOUR_TOKEN_ID"
TOKEN_SECRET = "YOUR_TOKEN_SECRET"
COMPANY_ID = "YOUR_COMPANY_ID_OR_SLUG"
PROJECT_ID = "YOUR_PROJECT_ID_OR_SLUG"
BASE_URL = "https://api.blue.cc"
# Headers for Blue API
HEADERS = {
"X-Bloo-Token-ID": TOKEN_ID,
"X-Bloo-Token-Secret": TOKEN_SECRET,
"X-Bloo-Company-ID": COMPANY_ID,
"X-Bloo-Project-ID": PROJECT_ID,
}
# Step 1: Get upload credentials
def get_upload_credentials():
url = f"{BASE_URL}/uploads?filename={FILENAME}"
response = requests.get(url, headers=HEADERS)
if response.status_code != 200:
raise Exception(f"Failed to fetch upload credentials: {response.status_code} - {response.text}")
return response.json()
# Step 2: Upload file to S3
def upload_to_s3(credentials):
s3_url = credentials["url"]
fields = credentials["fields"]
files = {
"acl": (None, fields["acl"]),
"Content-Disposition": (None, fields["Content-Disposition"]),
"Key": (None, fields["Key"]),
"X-Key": (None, fields["X-Key"]),
"Content-Type": (None, fields["Content-Type"]),
"bucket": (None, fields["bucket"]),
"X-Amz-Algorithm": (None, fields["X-Amz-Algorithm"]),
"X-Amz-Credential": (None, fields["X-Amz-Credential"]),
"X-Amz-Date": (None, fields["X-Amz-Date"]),
"Policy": (None, fields["Policy"]),
"X-Amz-Signature": (None, fields["X-Amz-Signature"]),
"file": (FILENAME, open(FILENAME, "rb"), fields["Content-Type"])
}
response = requests.post(s3_url, files=files)
if response.status_code != 204:
raise Exception(f"S3 upload failed: {response.status_code} - {response.text}")
print("S3 upload successful")
# Step 3: Register file with Blue
def register_file(file_uid):
graphql_url = f"{BASE_URL}/graphql"
headers = HEADERS.copy()
headers["Content-Type"] = "application/json"
query = """
mutation CreateFile($uid: String!, $name: String!, $type: String!, $extension: String!, $size: Float!, $projectId: String!, $companyId: String!) {
createFile(input: {uid: $uid, name: $name, type: $type, size: $size, extension: $extension, projectId: $projectId, companyId: $companyId}) {
id
uid
name
__typename
}
}
"""
variables = {
"uid": file_uid,
"name": FILENAME,
"type": "image/jpeg",
"extension": "jpg",
"size": float(os.path.getsize(FILENAME)), # Dynamic file size
"projectId": PROJECT_ID,
"companyId": COMPANY_ID
}
payload = {
"operationName": "CreateFile",
"query": query,
"variables": variables
}
response = requests.post(graphql_url, headers=headers, json=payload)
if response.status_code != 200:
raise Exception(f"GraphQL registration failed: {response.status_code} - {response.text}")
print("File registration successful:", response.json())
# Main execution
def main():
try:
if not os.path.exists(FILENAME):
raise Exception(f"File '{FILENAME}' not found")
# Step 1: Fetch credentials
credentials = get_upload_credentials()
print("Upload credentials fetched:", credentials)
# Step 2: Upload to S3
upload_to_s3(credentials)
# Step 3: Register file
file_uid = credentials["fields"]["Key"].split("/")[0]
register_file(file_uid)
print("Upload completed successfully!")
except Exception as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()
https://api.blue.cc/uploads?filename=test.jpg
Sample Response:
{
"url": "https://s3.ap-southeast-1.amazonaws.com/bloo-uploads",
"fields": {
"acl": "private",
"Content-Disposition": "attachment; filename=\"test.jpg\"",
"Key": "cm8qujq3b01p22lrv9xbb2q62/test.jpg",
"X-Key": "cm8qujq3b01p22lrv9xbb2q62",
"Content-Type": "image/jpeg",
"bucket": "bloo-uploads",
"X-Amz-Algorithm": "AWS4-HMAC-SHA256",
"X-Amz-Credential": "AKIA2LAMM6FPGT53RMQY/20250327/ap-southeast-1/s3/aws4_request",
"X-Amz-Date": "20250327T042138Z",
"Policy": "...",
"X-Amz-Signature": "e1d2446a0bdbfcbd3a73f926c7d92c87460f8d8ae030d717355f2790d6609452"
}
}
files
parameter in requests.post
to send a multipart/form-data request to the S3 URLhttps://api.blue.cc/graphql
os.path.getsize
Sample Response:
{
"data": {
"createFile": {
"id": "cm8qudguy2ouprv2ljq3widd8",
"uid": "cm8qujq3b01p22lrv9xbb2q62",
"name": "test.jpg",
"__typename": "File"
}
}
}
import requests
import json
import os
# Configuration
FILENAME = "test.jpg"
TOKEN_ID = "YOUR_TOKEN_ID"
TOKEN_SECRET = "YOUR_TOKEN_SECRET"
COMPANY_ID = "YOUR_COMPANY_ID_OR_SLUG"
PROJECT_ID = "YOUR_PROJECT_ID_OR_SLUG"
BASE_URL = "https://api.blue.cc"
# Headers for Blue API
HEADERS = {
"X-Bloo-Token-ID": TOKEN_ID,
"X-Bloo-Token-Secret": TOKEN_SECRET,
"X-Bloo-Company-ID": COMPANY_ID,
"X-Bloo-Project-ID": PROJECT_ID,
}
# Step 1: Get upload credentials
def get_upload_credentials():
url = f"{BASE_URL}/uploads?filename={FILENAME}"
response = requests.get(url, headers=HEADERS)
if response.status_code != 200:
raise Exception(f"Failed to fetch upload credentials: {response.status_code} - {response.text}")
return response.json()
# Step 2: Upload file to S3
def upload_to_s3(credentials):
s3_url = credentials["url"]
fields = credentials["fields"]
files = {
"acl": (None, fields["acl"]),
"Content-Disposition": (None, fields["Content-Disposition"]),
"Key": (None, fields["Key"]),
"X-Key": (None, fields["X-Key"]),
"Content-Type": (None, fields["Content-Type"]),
"bucket": (None, fields["bucket"]),
"X-Amz-Algorithm": (None, fields["X-Amz-Algorithm"]),
"X-Amz-Credential": (None, fields["X-Amz-Credential"]),
"X-Amz-Date": (None, fields["X-Amz-Date"]),
"Policy": (None, fields["Policy"]),
"X-Amz-Signature": (None, fields["X-Amz-Signature"]),
"file": (FILENAME, open(FILENAME, "rb"), fields["Content-Type"])
}
response = requests.post(s3_url, files=files)
if response.status_code != 204:
raise Exception(f"S3 upload failed: {response.status_code} - {response.text}")
print("S3 upload successful")
# Step 3: Register file with Blue
def register_file(file_uid):
graphql_url = f"{BASE_URL}/graphql"
headers = HEADERS.copy()
headers["Content-Type"] = "application/json"
query = """
mutation CreateFile($uid: String!, $name: String!, $type: String!, $extension: String!, $size: Float!, $projectId: String!, $companyId: String!) {
createFile(input: {uid: $uid, name: $name, type: $type, size: $size, extension: $extension, projectId: $projectId, companyId: $companyId}) {
id
uid
name
__typename
}
}
"""
variables = {
"uid": file_uid,
"name": FILENAME,
"type": "image/jpeg",
"extension": "jpg",
"size": float(os.path.getsize(FILENAME)),
"projectId": PROJECT_ID,
"companyId": COMPANY_ID
}
payload = {
"operationName": "CreateFile",
"query": query,
"variables": variables
}
response = requests.post(graphql_url, headers=headers, json=payload)
if response.status_code != 200:
raise Exception(f"GraphQL registration failed: {response.status_code} - {response.text}")
print("File registration successful:", response.json())
return file_uid
# Step 4: Associate file with Todo Custom Field
def associate_file_with_todo(file_uid):
graphql_url = f"{BASE_URL}/graphql"
headers = HEADERS.copy()
headers["Content-Type"] = "application/json"
query = """
mutation CreateTodoCustomFieldFile($input: CreateTodoCustomFieldFileInput!) {
createTodoCustomFieldFile(input: $input)
}
"""
variables = {
"input": {
"todoId": "YOUR_TODO_ID",
"customFieldId": "YOUR_CUSTOM_FIELD_ID",
"fileUid": file_uid
}
}
payload = {
"operationName": "CreateTodoCustomFieldFile",
"query": query,
"variables": variables
}
response = requests.post(graphql_url, headers=headers, json=payload)
if response.status_code != 200:
raise Exception(f"Todo association failed: {response.status_code} - {response.text}")
print("Todo association successful:", response.json())
# Main execution
def main():
try:
if not os.path.exists(FILENAME):
raise Exception(f"File '{FILENAME}' not found")
# Step 1: Fetch credentials
credentials = get_upload_credentials()
print("Upload credentials fetched:", credentials)
# Step 2: Upload to S3
upload_to_s3(credentials)
# Step 3: Register file
file_uid = credentials["fields"]["Key"].split("/")[0]
register_file(file_uid)
# Step 4: Associate with Todo
associate_file_with_todo(file_uid)
print("Upload completed successfully!")
except Exception as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()
Steps 1-3 are the same as the File Tab process (fetch credentials, upload to S3, and register file metadata).
todoId
and customFieldId
specific to your setupSample Response:
{
"data": {
"createTodoCustomFieldFile": true
}
}