An Attachment is a file asset that is attached to another Kantata OX object. Depending on the type of object, the file is used or displayed in different ways. The objects that Attachments can be attached to are: Post and Expense.
To create an Expense or Post with an Attachment, use the resulting attachment_id from the Attachment creation request in the parameters of the Expense or Post creation request.
There are two different ways to upload file attachments. Direct CDN upload, or Kantata OX server upload.
Uploading directly to the CDN is a three step process but allows for faster uploading, significantly larger attachments, and more accurate upload progress information.
Retrieving upload credentials has two required fields, direct and attachment. The direct field must be set to true. The attachment field must include two attributes:
type(required) must bepost_attachmentorreceipt.filename(required) is the filename of the file to be uploaded.
The file name must not contain spaces or special characters other than an underscore ("_") or a hyphen ("-"), otherwise the file upload will fail.
After successfully creating an attachment, an upload url and credentials will be returned in JSON format with an HTTP 200 status code.
curl --form "direct=true" --form "attachment[filename]=attachment.doc"
--form "attachment[type]=post_attachment" "https://api.mavenlink.com/api/v1/attachments.json"The response JSON will then contain the information to upload the file:
{
"id": 2,
"action": "[upload url]",
"fields":{
"utf8": "✓",
"key": " ... ",
"Content-Disposition": "attachment; filename="attachment.doc"; filename*=UTF-8''attachment.doc",
"success_action_status": 201,
"AWSAccessKeyId": " ... ",
"acl": "private",
"policy": " ... ",
"signature": " ... "
}
}A second POST request can then be made to the provided action with the field values returned in the JSON response, in addition to the file data. The AWSAccessKeyId field value is only active for 5 seconds, so these consecutive calls are meant to be made in quick succession via a script.
Other than $file, variables in the request that start with $ are the values represented by the " ... " blanks in the JSON response.
curl -X POST "[upload url]" -H 'cache-control: no-cache' --form 'AWSAccessKeyId='$awsKey --form 'Content-Disposition="attachment; filename="$file"; filename*=UTF-8''''''$file"' --form 'x-amz-server-side-encryption=AES256' --form 'key='$key --form 'policy='$policy --form 'signature='$sig --form 'acl=private' --form 'success_action_status=201' --form 'utf8=✓' --form 'file=@'$fileFinally, once the upload has completed successfully, a 'sync' request (specified below) is necessary to mark the upload as complete.
Below is a simplified example using ruby to achieve the same thing as with the curl commands above:
#!/usr/bin/env ruby
require 'net/https'
require "uri"
require 'json'
TOKEN="Bearer <your-secret-here>"
ATTACHMENTS_ENDPOINT="https://api.mavenlink.com/api/v1/attachments.json"
BOUNDARY = "AaB03x" # Make sure it is not present in the file you're uploading.
file = ARGV[0]
filename = File.basename(file)
uri = URI.parse(ATTACHMENTS_ENDPOINT)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri.request_uri)
request['Authorization'] = TOKEN
request.set_form_data(first_request_params)
response = http.request(request)
first_response = JSON.parse(response.body)
# for the second request we manually build the request body
post_body = []
second_request_params = first_response["fields"].merge({ "file" => File.open(file) })
second_request_params.each do |k,v|
post_body << "--#{BOUNDARY}\r\n"
if v.is_a?(IO)
post_body << "Content-Disposition: form-data; name="#{k}"; filename="#{filename}"\r\n"
post_body << "\r\n"
post_body << File.read(file)
post_body << "\r\n"
else
post_body << "Content-Disposition: form-data; name="#{k}"\r\n\r\n"
post_body << v.to_s + "\r\n"
end
end
post_body << "--#{BOUNDARY}--\r\n"
uri = URI.parse(first_response["action"])
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri.request_uri)
request.body = post_body.join
request["Content-Type"] = "multipart/form-data, boundary=#{BOUNDARY}"
response = http.request(request)
exit(response.code == second_request_params["success_action_status"].to_s)Syncing marks an upload as complete, and verifies its existence on the CDN.
A sync request can be made as follows:
curl -X PUT "https://api.mavenlink.com/api/v1/attachments/2/sync.json"For small attachments (< 10MB), you may upload to our servers and skip the 3-step direct CDN upload process. Doing so requires one field, attachment, with two required attributes:
type(required) must be eitherpost_attachmentorreceipt.data(required) is the multipart/form-data encoded file contents.
After successfully creating an attachment its metadata will be returned in JSON format with an HTTP 200 status code. To upload a file using the curl utility, you would run a command like this one:
curl --form "attachment[data]=@test.rb" --form "attachment[type]=post_attachment" "https://api.mavenlink.com/api/v1/attachments.json"This endpoint returns structured Attachment objects. As with all Kantata OX API endpoints, the returned data will be referenced in sorted order in the results array and will be indexed by ID in the attachments top-level JSON key. Please see our Response Format section for more information.
curl -i -X POST \
https://api.mavenlink.com/api/v1/attachments \
-H 'Content-Type: application/json' \
-d '{
"attachment": {
"type": "string",
"direct": true,
"data": "string",
"filename": "string"
}
}'{ "count": 0, "meta": { "count": 0, "page_count": 0, "page_number": 0, "page_size": 0 }, "results": [ { "key": "string", "id": "string" } ], "attachments": { "property1": { "created_at": "2019-08-24T14:15:22Z", "creator_name": "string", "deletable": true, "deleted_at": "2019-08-24T14:15:22Z", "filename": "string", "filesize": 0, "previewable": true, "updated_at": "2019-08-24T14:15:22Z", "url": "string", "view_in_new_tab": true }, "property2": { "created_at": "2019-08-24T14:15:22Z", "creator_name": "string", "deletable": true, "deleted_at": "2019-08-24T14:15:22Z", "filename": "string", "filesize": 0, "previewable": true, "updated_at": "2019-08-24T14:15:22Z", "url": "string", "view_in_new_tab": true } } }
An Attachment is a file asset that is attached to another Kantata OX object. Depending on the type of object, the file is used or displayed in different ways. The objects that Attachments can be attached to are: Post and Expense.
To create an Expense or Post with an Attachment, use the resulting attachment_id from the Attachment creation request in the parameters of the Expense or Post creation request.
There are two different ways to upload file attachments. Direct CDN upload, or Kantata OX server upload.
Uploading directly to the CDN is a three step process but allows for faster uploading, significantly larger attachments, and more accurate upload progress information.
Retrieving upload credentials has two required fields, direct and attachment. The direct field must be set to true. The attachment field must include two attributes:
type(required) must bepost_attachmentorreceipt.filename(required) is the filename of the file to be uploaded.
The file name must not contain spaces or special characters other than an underscore ("_") or a hyphen ("-"), otherwise the file upload will fail.
After successfully creating an attachment, an upload url and credentials will be returned in JSON format with an HTTP 200 status code.
curl --form "direct=true" --form "attachment[filename]=attachment.doc"
--form "attachment[type]=post_attachment" "https://api.mavenlink.com/api/v1/attachments.json"The response JSON will then contain the information to upload the file:
{
"id": 2,
"action": "[upload url]",
"fields":{
"utf8": "✓",
"key": " ... ",
"Content-Disposition": "attachment; filename="attachment.doc"; filename*=UTF-8''attachment.doc",
"success_action_status": 201,
"AWSAccessKeyId": " ... ",
"acl": "private",
"policy": " ... ",
"signature": " ... "
}
}A second POST request can then be made to the provided action with the field values returned in the JSON response, in addition to the file data. The AWSAccessKeyId field value is only active for 5 seconds, so these consecutive calls are meant to be made in quick succession via a script.
Other than $file, variables in the request that start with $ are the values represented by the " ... " blanks in the JSON response.
curl -X POST "[upload url]" -H 'cache-control: no-cache' --form 'AWSAccessKeyId='$awsKey --form 'Content-Disposition="attachment; filename="$file"; filename*=UTF-8''''''$file"' --form 'x-amz-server-side-encryption=AES256' --form 'key='$key --form 'policy='$policy --form 'signature='$sig --form 'acl=private' --form 'success_action_status=201' --form 'utf8=✓' --form 'file=@'$fileFinally, once the upload has completed successfully, a 'sync' request (specified below) is necessary to mark the upload as complete.
Below is a simplified example using ruby to achieve the same thing as with the curl commands above:
#!/usr/bin/env ruby
require 'net/https'
require "uri"
require 'json'
TOKEN="Bearer <your-secret-here>"
ATTACHMENTS_ENDPOINT="https://api.mavenlink.com/api/v1/attachments.json"
BOUNDARY = "AaB03x" # Make sure it is not present in the file you're uploading.
file = ARGV[0]
filename = File.basename(file)
uri = URI.parse(ATTACHMENTS_ENDPOINT)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri.request_uri)
request['Authorization'] = TOKEN
request.set_form_data(first_request_params)
response = http.request(request)
first_response = JSON.parse(response.body)
# for the second request we manually build the request body
post_body = []
second_request_params = first_response["fields"].merge({ "file" => File.open(file) })
second_request_params.each do |k,v|
post_body << "--#{BOUNDARY}\r\n"
if v.is_a?(IO)
post_body << "Content-Disposition: form-data; name="#{k}"; filename="#{filename}"\r\n"
post_body << "\r\n"
post_body << File.read(file)
post_body << "\r\n"
else
post_body << "Content-Disposition: form-data; name="#{k}"\r\n\r\n"
post_body << v.to_s + "\r\n"
end
end
post_body << "--#{BOUNDARY}--\r\n"
uri = URI.parse(first_response["action"])
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri.request_uri)
request.body = post_body.join
request["Content-Type"] = "multipart/form-data, boundary=#{BOUNDARY}"
response = http.request(request)
exit(response.code == second_request_params["success_action_status"].to_s)Syncing marks an upload as complete, and verifies its existence on the CDN.
A sync request can be made as follows:
curl -X PUT "https://api.mavenlink.com/api/v1/attachments/2/sync.json"For small attachments (< 10MB), you may upload to our servers and skip the 3-step direct CDN upload process. Doing so requires one field, attachment, with two required attributes:
type(required) must be eitherpost_attachmentorreceipt.data(required) is the multipart/form-data encoded file contents.
After successfully creating an attachment its metadata will be returned in JSON format with an HTTP 200 status code. To upload a file using the curl utility, you would run a command like this one:
curl --form "attachment[data]=@test.rb" --form "attachment[type]=post_attachment" "https://api.mavenlink.com/api/v1/attachments.json"This endpoint returns structured Attachment objects. As with all Kantata OX API endpoints, the returned data will be referenced in sorted order in the results array and will be indexed by ID in the attachments top-level JSON key. Please see our Response Format section for more information.
curl -i -X POST \
https://api.mavenlink.com/api/v1/attachments \
-H 'Content-Type: application/json' \
-d '{
"attachment": {
"type": "string",
"direct": true,
"data": "string",
"filename": "string"
}
}'{ "count": 0, "meta": { "count": 0, "page_count": 0, "page_number": 0, "page_size": 0 }, "results": [ { "key": "string", "id": "string" } ], "attachments": { "property1": { "created_at": "2019-08-24T14:15:22Z", "creator_name": "string", "deletable": true, "deleted_at": "2019-08-24T14:15:22Z", "filename": "string", "filesize": 0, "previewable": true, "updated_at": "2019-08-24T14:15:22Z", "url": "string", "view_in_new_tab": true }, "property2": { "created_at": "2019-08-24T14:15:22Z", "creator_name": "string", "deletable": true, "deleted_at": "2019-08-24T14:15:22Z", "filename": "string", "filesize": 0, "previewable": true, "updated_at": "2019-08-24T14:15:22Z", "url": "string", "view_in_new_tab": true } } }