Diamra Developer Docs
All pages

Estimates

An estimate prices a project for your customer. Diamra reads the piece from the project's design image and its measurements, and lists the questions whose answers would change the price. You save the customer's answers, change them as often as they like, and ask for a price when you want one, with your markup. The estimate keeps its id the whole way, through every answer and every new price. A project can have many estimates: a new one starts only when you ask for one, and the earlier ones stay as they were. Every call takes your API key and the customer's access token from Auth.

How an estimate works

  1. Create an estimate for a project. Diamra reads the design, which takes about half a minute. Until then its status is processing. If nothing would change the price, it goes on to priced; otherwise it stops at requires_answers.
  2. Read its questions and save the customer's answers. Saving prices nothing, and the customer can change an answer as often as they like.
  3. When you want a number, price the estimate. It takes about ten seconds, with whatever answers it has, and the estimate is priced.
  4. After a change of answer, price it again for a new price. The estimate keeps its id.
  5. Create another estimate whenever you want a fresh reading of the design. It gets the next revision and becomes the newest, and the customer's answers carry over to it.

Create an estimate

Starts a new estimate of the project. It becomes the project's newest.

POSThttps://api.diamra.com/v1/estimates
HeaderWhat it is
Authorization
Required. Bearer and your API key.
Diamra-Customer-Token
Required. The customer's access_token from Log in, Register or Refresh.
ParameterWhat it is
project
Required. The project to price, as an object.
project.id
Required. The project's id from Create a project.
RequestShell
curl https://api.diamra.com/v1/estimates \  -H "Authorization: Bearer $DIAMRA_API_KEY" \  -H "Diamra-Customer-Token: $ACCESS_TOKEN" \  -H "Content-Type: application/json" \  -d '{    "project": {      "id": "8d3e5b17-2f6a-4c9e-b0d4-7a1c9e2f6b58"    }  }'
ResponseHTTP
HTTP/2 202content-type: application/jsonrequest-id: req_2c7f0a9e41b3d568{  "id": "c3e8a1f4-7b2d-4c6e-9a0f-5d1b8e3c2a74",  "project": {    "id": "8d3e5b17-2f6a-4c9e-b0d4-7a1c9e2f6b58"  },  "revision": 1,  "is_newest": true,  "status": "processing",  "timing": {    "elapsed_ms": 1210,    "expected_ms": 21186  },  "created_at": 1794325212,  "price": null,  "currency": "USD",  "confidence": null,  "piece_type": null,  "style": null,  "setting": null,  "metal": null,  "metal_color": null,  "metal_options": [],  "center_stone": null,  "total_carat_weight": null,  "ring_size": null,  "from": null,  "stones": [],  "accepted": null,  "decline_reason": null}

One estimate of a project runs at a time: while one is processing, a new one answers 409 estimate_in_progress. Your store decides whether customers may ask for estimates, with the Estimate switch in your Bench's project settings. While it's off, the call answers 403 estimates_disabled.

Retrieve an estimate

Returns the estimate. While its status is processing, ask again every few seconds. Its timing says how long it has run and how long one takes now.

GEThttps://api.diamra.com/v1/estimates/{id}

Send the same two headers.

RequestShell
curl https://api.diamra.com/v1/estimates/c3e8a1f4-7b2d-4c6e-9a0f-5d1b8e3c2a74 \  -H "Authorization: Bearer $DIAMRA_API_KEY" \  -H "Diamra-Customer-Token: $ACCESS_TOKEN"
ResponseHTTP
HTTP/2 200content-type: application/jsonrequest-id: req_8b1d4f6a20c97e35{  "id": "c3e8a1f4-7b2d-4c6e-9a0f-5d1b8e3c2a74",  "project": {    "id": "8d3e5b17-2f6a-4c9e-b0d4-7a1c9e2f6b58"  },  "revision": 1,  "is_newest": true,  "status": "requires_answers",  "timing": null,  "created_at": 1794325212,  "price": null,  "currency": "USD",  "confidence": 58,  "piece_type": "ring",  "style": "halo_ring",  "setting": "prong",  "metal": null,  "metal_color": null,  "metal_options": [],  "center_stone": {    "type": "diamond",    "shape": "oval",    "carat_weight": 1,    "count": 1,    "origin": null  },  "total_carat_weight": 1.36,  "ring_size": 7,  "from": {    "piece_type": "design_image",    "style": "design_image",    "setting": "design_image",    "metal": null,    "metal_color": null,    "center_stone": {      "type": "design_image",      "shape": "design_image",      "carat_weight": "customer_prompt",      "count": "design_image",      "origin": null    },    "total_carat_weight": "design_image",    "ring_size": "default"  },  "stones": [    {      "type": "diamond",      "shape": "oval",      "count": 1,      "carat_each": 1,      "carat_total": 1,      "diameter_mm": null,      "length_mm": 7.7,      "width_mm": 5.7,      "setting": "prong",      "is_center": true,      "origin": null,      "from": {        "type": "design_image",        "shape": "design_image",        "count": "design_image",        "carat_each": "customer_prompt",        "carat_total": "customer_prompt",        "setting": "design_image",        "origin": null      }    },    {      "type": "diamond",      "shape": "round_brilliant",      "count": 18,      "carat_each": 0.02,      "carat_total": 0.36,      "diameter_mm": 1.7,      "length_mm": null,      "width_mm": null,      "setting": "pave",      "is_center": false,      "origin": null,      "from": {        "type": "design_image",        "shape": "design_image",        "count": "design_image",        "carat_each": "design_image",        "carat_total": "design_image",        "setting": "design_image",        "origin": null      }    }  ],  "accepted": null,  "decline_reason": null}

Once the design is read, the estimate describes the piece and each group of stones, even before it has a price. The metal and the stones' origin come with the price.

List a project's estimates

Returns every estimate of the project, newest first. The current one has is_newest: show it as the price, and the others as the project's history.

GEThttps://api.diamra.com/v1/estimates

Send the same two headers. The parameters go in the query string.

ParameterWhat it is
project
Required. The project's id.
is_newest
true returns only the newest estimate.
RequestShell
curl -G https://api.diamra.com/v1/estimates \  -H "Authorization: Bearer $DIAMRA_API_KEY" \  -H "Diamra-Customer-Token: $ACCESS_TOKEN" \  -d project=8d3e5b17-2f6a-4c9e-b0d4-7a1c9e2f6b58 \  -d is_newest=true
ResponseHTTP
HTTP/2 200content-type: application/jsonrequest-id: req_6d3b8e0f52a14c97{  "object": "list",  "url": "/v1/estimates",  "has_more": false,  "data": [    {      "id": "c3e8a1f4-7b2d-4c6e-9a0f-5d1b8e3c2a74",      "project": {        "id": "8d3e5b17-2f6a-4c9e-b0d4-7a1c9e2f6b58"      },      "revision": 1,      "is_newest": true,      "status": "priced",      "timing": null,      "created_at": 1794325212,      "price": 2450,      "currency": "USD",      "confidence": 82,      "piece_type": "ring",      "style": "halo_ring",      "setting": "prong",      "metal": "14K Gold",      "metal_color": "yellow",      "metal_options": [        {          "metal": "18K Gold",          "price": 2975        },        {          "metal": "Platinum",          "price": 3350        }      ],      "center_stone": {        "type": "diamond",        "shape": "oval",        "carat_weight": 1,        "count": 1,        "origin": "lab_grown"      },      "total_carat_weight": 1.36,      "ring_size": 7,      "from": {        "piece_type": "design_image",        "style": "design_image",        "setting": "design_image",        "metal": "customer_answers",        "metal_color": "customer_prompt",        "center_stone": {          "type": "design_image",          "shape": "design_image",          "carat_weight": "customer_prompt",          "count": "design_image",          "origin": "customer_answers"        },        "total_carat_weight": "design_image",        "ring_size": "default"      },      "stones": [        {          "type": "diamond",          "shape": "oval",          "count": 1,          "carat_each": 1,          "carat_total": 1,          "diameter_mm": null,          "length_mm": 7.7,          "width_mm": 5.7,          "setting": "prong",          "is_center": true,          "origin": "lab_grown",          "from": {            "type": "design_image",            "shape": "design_image",            "count": "design_image",            "carat_each": "customer_prompt",            "carat_total": "customer_prompt",            "setting": "design_image",            "origin": "customer_answers"          }        },        {          "type": "diamond",          "shape": "round_brilliant",          "count": 18,          "carat_each": 0.02,          "carat_total": 0.36,          "diameter_mm": 1.7,          "length_mm": null,          "width_mm": null,          "setting": "pave",          "is_center": false,          "origin": "lab_grown",          "from": {            "type": "design_image",            "shape": "design_image",            "count": "design_image",            "carat_each": "design_image",            "carat_total": "design_image",            "setting": "design_image",            "origin": "default"          }        }      ],      "accepted": null,      "decline_reason": null    }  ]}

List an estimate's questions

Returns the questions whose answers would make the price more exact, the most useful first. Each one says who it's for: customer questions are the customer's to answer, and owner questions are for your team, who answer them on your Bench.

GEThttps://api.diamra.com/v1/estimates/{id}/questions

Send the same two headers.

ParameterWhat it is
is_answered
false returns the questions still open, and true the ones answered. The default is false.
RequestShell
curl https://api.diamra.com/v1/estimates/c3e8a1f4-7b2d-4c6e-9a0f-5d1b8e3c2a74/questions \  -H "Authorization: Bearer $DIAMRA_API_KEY" \  -H "Diamra-Customer-Token: $ACCESS_TOKEN"
ResponseHTTP
HTTP/2 200content-type: application/jsonrequest-id: req_1e6c9a3f57d20b84{  "object": "list",  "url": "/v1/estimates/c3e8a1f4-7b2d-4c6e-9a0f-5d1b8e3c2a74/questions",  "has_more": false,  "data": [    {      "key": "metal.karat",      "label": "Metal karat",      "question": "Which karat of gold?",      "type": "text",      "options": [        {          "label": "10K",          "value": "10K"        },        {          "label": "14K",          "value": "14K"        },        {          "label": "18K",          "value": "18K"        },        {          "label": "22K",          "value": "22K"        }      ],      "intended_for": "customer",      "is_answered": false    },    {      "key": "stones.center.stone_type",      "label": "Natural or lab-grown · center diamond",      "question": "Should the center diamond be natural or lab-grown?",      "type": "text",      "options": [        {          "label": "Natural diamond",          "value": "Natural diamond"        },        {          "label": "Lab-grown diamond",          "value": "Lab-grown diamond"        }      ],      "intended_for": "customer",      "is_answered": false    },    {      "key": "features.engraving.present",      "label": "Engraving",      "question": "Do you want an engraving?",      "type": "boolean",      "options": [],      "intended_for": "customer",      "is_answered": false    },    {      "key": "stones.diamond_round_brilliant_18.setting_type",      "label": "Setting · 18 round diamonds",      "question": "How should the 18 round diamonds be set?",      "type": "text",      "options": [        {          "label": "Prongs",          "value": "Prongs"        },        {          "label": "Bezel",          "value": "Bezel"        },        {          "label": "Halo",          "value": "Halo"        },        {          "label": "Pave",          "value": "Pave"        },        {          "label": "Channel",          "value": "Channel"        }      ],      "intended_for": "owner",      "is_answered": false    }  ]}

Show each customer question with its options as answers to tap, and keep a field open beside them: an answer doesn't have to be one of the options.

Answer questions

Saves the customer's answers, or changes the ones they gave before. It prices nothing. Only the project's newest estimate takes answers: to its customer questions, and to any question the customer answered before. An answer your team gave on your Bench stays as they set it.

POSThttps://api.diamra.com/v1/estimates/{id}/answers

Send the same two headers.

ParameterWhat it is
answers
Required. From 1 to 20 answers, as a list.
answers[].key
Required. The question's key, exactly as given.
answers[].value
The answer, as the question's type says: a string for text, a whole number for integer, a number for decimal, or true or false for boolean. An option's value fits.
answers[].unknown
true when the customer doesn't know. Send it in place of value.
RequestShell
curl https://api.diamra.com/v1/estimates/c3e8a1f4-7b2d-4c6e-9a0f-5d1b8e3c2a74/answers \  -H "Authorization: Bearer $DIAMRA_API_KEY" \  -H "Diamra-Customer-Token: $ACCESS_TOKEN" \  -H "Content-Type: application/json" \  -d '{    "answers": [      {        "key": "metal.karat",        "value": "14K"      },      {        "key": "stones.center.stone_type",        "value": "Lab-grown diamond"      },      {        "key": "features.engraving.present",        "unknown": true      }    ]  }'
ResponseHTTP
HTTP/2 200content-type: application/jsonrequest-id: req_4a9e2c5d71f0b386{  "object": "list",  "url": "/v1/estimates/c3e8a1f4-7b2d-4c6e-9a0f-5d1b8e3c2a74/answers",  "has_more": false,  "data": [    {      "key": "metal.karat",      "question": "Which karat of gold?",      "type": "text",      "options": [        {          "label": "10K",          "value": "10K"        },        {          "label": "14K",          "value": "14K"        },        {          "label": "18K",          "value": "18K"        },        {          "label": "22K",          "value": "22K"        }      ],      "value": "14K",      "unknown": false,      "answered_by": "customer",      "answered_at": 1794325290    },    {      "key": "stones.center.stone_type",      "question": "Should the center diamond be natural or lab-grown?",      "type": "text",      "options": [        {          "label": "Natural diamond",          "value": "Natural diamond"        },        {          "label": "Lab-grown diamond",          "value": "Lab-grown diamond"        }      ],      "value": "Lab-grown diamond",      "unknown": false,      "answered_by": "customer",      "answered_at": 1794325290    },    {      "key": "features.engraving.present",      "question": "Do you want an engraving?",      "type": "boolean",      "options": [],      "value": null,      "unknown": true,      "answered_by": "customer",      "answered_at": 1794325290    }  ]}

The call saves every answer or none, and answers the project's answers as they stand, the same list as List answers. To change an answer, send its key with the new value. Sending the same value again changes nothing. unknown can't take back an answer the customer gave: send a value instead.

List answers

Returns the answers the next price uses, oldest first, the customer's and your team's. Answers belong to the project, so every estimate of it lists the same ones. Each comes with its question's type and options, so you can show it with a way to change it.

GEThttps://api.diamra.com/v1/estimates/{id}/answers

Send the same two headers.

RequestShell
curl https://api.diamra.com/v1/estimates/c3e8a1f4-7b2d-4c6e-9a0f-5d1b8e3c2a74/answers \  -H "Authorization: Bearer $DIAMRA_API_KEY" \  -H "Diamra-Customer-Token: $ACCESS_TOKEN"
ResponseHTTP
HTTP/2 200content-type: application/jsonrequest-id: req_9f2b5d8e03a61c47{  "object": "list",  "url": "/v1/estimates/c3e8a1f4-7b2d-4c6e-9a0f-5d1b8e3c2a74/answers",  "has_more": false,  "data": [    {      "key": "metal.karat",      "question": "Which karat of gold?",      "type": "text",      "options": [        {          "label": "10K",          "value": "10K"        },        {          "label": "14K",          "value": "14K"        },        {          "label": "18K",          "value": "18K"        },        {          "label": "22K",          "value": "22K"        }      ],      "value": "14K",      "unknown": false,      "answered_by": "customer",      "answered_at": 1794325290    },    {      "key": "stones.center.stone_type",      "question": "Should the center diamond be natural or lab-grown?",      "type": "text",      "options": [        {          "label": "Natural diamond",          "value": "Natural diamond"        },        {          "label": "Lab-grown diamond",          "value": "Lab-grown diamond"        }      ],      "value": "Lab-grown diamond",      "unknown": false,      "answered_by": "customer",      "answered_at": 1794325290    },    {      "key": "features.engraving.present",      "question": "Do you want an engraving?",      "type": "boolean",      "options": [],      "value": null,      "unknown": true,      "answered_by": "customer",      "answered_at": 1794325290    }  ]}

Price an estimate

Prices the estimate with the answers it has, whether all its questions are answered or none. It is the only way an estimate gets a price after its reading. It answers the same estimate, now processing.

POSThttps://api.diamra.com/v1/estimates/{id}/price

Send the same two headers. It takes no parameters.

RequestShell
curl -X POST https://api.diamra.com/v1/estimates/c3e8a1f4-7b2d-4c6e-9a0f-5d1b8e3c2a74/price \  -H "Authorization: Bearer $DIAMRA_API_KEY" \  -H "Diamra-Customer-Token: $ACCESS_TOKEN"
ResponseHTTP
HTTP/2 200content-type: application/jsonrequest-id: req_3d8f1a6c94e27b05{  "id": "c3e8a1f4-7b2d-4c6e-9a0f-5d1b8e3c2a74",  "project": {    "id": "8d3e5b17-2f6a-4c9e-b0d4-7a1c9e2f6b58"  },  "revision": 1,  "is_newest": true,  "status": "processing",  "timing": {    "elapsed_ms": 960,    "expected_ms": 5683  },  "created_at": 1794325212,  "price": null,  "currency": "USD",  "confidence": 58,  "piece_type": "ring",  "style": "halo_ring",  "setting": "prong",  "metal": null,  "metal_color": null,  "metal_options": [],  "center_stone": {    "type": "diamond",    "shape": "oval",    "carat_weight": 1,    "count": 1,    "origin": null  },  "total_carat_weight": 1.36,  "ring_size": 7,  "from": {    "piece_type": "design_image",    "style": "design_image",    "setting": "design_image",    "metal": null,    "metal_color": null,    "center_stone": {      "type": "design_image",      "shape": "design_image",      "carat_weight": "customer_prompt",      "count": "design_image",      "origin": null    },    "total_carat_weight": "design_image",    "ring_size": "default"  },  "stones": [    {      "type": "diamond",      "shape": "oval",      "count": 1,      "carat_each": 1,      "carat_total": 1,      "diameter_mm": null,      "length_mm": 7.7,      "width_mm": 5.7,      "setting": "prong",      "is_center": true,      "origin": null,      "from": {        "type": "design_image",        "shape": "design_image",        "count": "design_image",        "carat_each": "customer_prompt",        "carat_total": "customer_prompt",        "setting": "design_image",        "origin": null      }    },    {      "type": "diamond",      "shape": "round_brilliant",      "count": 18,      "carat_each": 0.02,      "carat_total": 0.36,      "diameter_mm": 1.7,      "length_mm": null,      "width_mm": null,      "setting": "pave",      "is_center": false,      "origin": null,      "from": {        "type": "design_image",        "shape": "design_image",        "count": "design_image",        "carat_each": "design_image",        "carat_total": "design_image",        "setting": "design_image",        "origin": null      }    }  ],  "accepted": null,  "decline_reason": null}

Only the project's newest estimate can be priced, once it's requires_answers or priced. A priced estimate is priced again only after an answer changed: otherwise the call answers 400 estimate_up_to_date.

The estimate

Retrieve an estimate, Create an estimate and Price an estimate answer it, and List a project's estimates returns them in data. Its piece fields have the same names and meaning as on the estimate.generated event, so one parser reads both.

FieldWhat it is
id
The estimate's id. It stays the same through its questions and every new price. Webhook events and the embedded studio's messages carry it as estimate_id.
project.id
The project the estimate prices.
revision
1 for the project's first estimate, then 2, 3 and so on.
is_newest
true on the project's newest estimate, or on the one before it when the newest failed. Answers and prices go to it.
status
Where the estimate is. See Statuses.
timing
While the status is processing: elapsed_ms, how long the reading or the pricing has run, and expected_ms, how long one takes now. A reading takes longer than a pricing. null otherwise. See Calls that take time.
created_at
When it started, in Unix seconds.
price
The price with your markup, as a whole number such as 2450, for the metal named in metal. null until priced.
currency
The price's currency. USD.
confidence
How sure the estimate is, as a whole number from 0 to 100. The lower it is, the more the price can still change. null until the design is read, and once your team confirms the price.
piece_type, style, setting, metal, metal_color, metal_options, center_stone, total_carat_weight, ring_size, from
The piece as the estimate reads it, as on the estimate.generated event. The words in from are under Where each detail came from. metal and metal_options come with the price.
stones
Each group of stones in the piece, the center stone first.
stones[].type, shape
What the stones are and their cut, such as diamond and oval, or sapphire and round_brilliant.
stones[].count
How many stones are in the group.
stones[].carat_each, carat_total
The carat weight of one stone, and of the whole group.
stones[].diameter_mm, length_mm, width_mm
Size in millimeters: the diameter of a round stone, or the length and width of any other shape. null when not known.
stones[].setting
How the stones are set, such as prong, bezel or pave.
stones[].is_center
true for the center stone.
stones[].origin
lab_grown or natural for diamonds: as priced, or as the design reads before a price. null for other gems.
stones[].from
Where type, shape, count, carat_each, carat_total, setting and origin came from, in the same words as from.
accepted
null, or { metal, price, accepted_at } once the customer accepts the price in your studio. accepted_at is in Unix seconds.
decline_reason
Why the piece can't be made, on a declined estimate. null otherwise.

Statuses

StatusWhat it means
processing
Diamra is reading the design or pricing it. A price it had stays until the new one is ready.
requires_answers
Read, not priced. Save the customer's answers, then price it.
priced
It has a price. Change or add answers, then price it again for a new one.
declined
The piece can't be made as designed. decline_reason says why.
failed
Something went wrong reading the design. Create a new estimate. A failed estimate is never the newest while an earlier one stands.

The question

FieldWhat it is
key
Send it back with the answer. Treat it as an opaque name.
label
A short name for the question, such as Ring size.
question
The question to show the customer.
type
text, integer, decimal or boolean: the kind of value the answer takes.
options
Answers to offer, each { label, value }. Show the label and send the value. Empty when the answer is open.
intended_for
customer, or owner for questions your team answers on your Bench.
is_answered
true once anyone answers it, or once the side it's for says they don't know. The customer can still change their own answer.

The answer

FieldWhat it is
key
The question it answers.
question
The question as it was asked.
type
text, integer, decimal or boolean, as on the question.
options
The question's answers to offer, each { label, value }. Empty when the answer is open.
value
The answer. null when unknown is true.
unknown
true when they said they don't know.
answered_by
customer, or owner for your team.
answered_at
When, in Unix seconds.

Showing the price

Show the newest estimate's price in currency with the metal it's for. While confidence is a number, the price can still move when your team reviews the piece, so show a line with it, such as "Your jeweler reviews every detail and confirms the final price with you." Once confidence is null, the price is final.

metal_options prices the same piece in other metals, and stones lists what goes into it. While the estimate has open customer questions, offer them under the price, and price it again once the customer answers: each answer makes it more exact.

Limits

One customer
5 calls a minute to Create an estimate, 10 to Answer questions and 3 to Price an estimate.
Your store
30 calls a minute to Create an estimate, 60 to Answer questions, 30 to Price an estimate, and 240 to the three lists together.

Over a limit, the call answers a 429 rate_limit, with Retry-After: 60. Retrieve an estimate has no limit.

Errors

AnswerWhen
400 parameter_missing
No project or no id in it, no answers, or an answer without a key. param names it.
400 parameter_unknown
A parameter the call doesn't take, often a typo. param names it.
400 parameter_invalid
project isn't an object or its id isn't a well-formed id; answers is empty or has more than 20; a key isn't a question for the customer, comes twice, or was answered by your team; a value doesn't fit its question's type; unknown sent for a question the customer answered; is_newest or is_answered isn't true or false. param names the one at fault.
400 resource_missing
project.id, or project in a list, isn't a project of this customer in your store. The message gives the id.
400 estimate_not_newest
Answers or a price for an estimate that isn't the project's newest.
400 estimate_unexpected_state
Answers or a price for a declined or failed estimate. The message says its status.
400 estimate_up_to_date
A price for an estimate that's priced with every answer it has. Change an answer first.
401 api_key_invalid
No key, or one Diamra doesn't know. See API keys.
401 api_key_revoked
The key was revoked in your console.
401 customer_token_invalid
No access token, or one that ran out or was never valid. Refresh the session, or log the customer in again.
403 access_pending
Your store approves new customers first and hasn't approved this one. Approve them under Customers in your Bench.
403 plan_required
Your store isn't on the Retail plan. Its keys work again once it is.
403 estimates_disabled
Your store doesn't let customers ask for estimates. Turn it on in your Bench's project settings.
404 resource_missing
No estimate with this id for this customer in your store.
409 estimate_in_progress
A new estimate or a price while an estimate of the project is processing. Try again once it's done.
429 rate_limit
Over a limit above.
503
The estimate couldn't be started. Try again in a minute.
An errorHTTP
HTTP/2 400content-type: application/jsonrequest-id: req_5e2a9c71b04d3f68{  "error": {    "type": "invalid_request_error",    "code": "estimate_not_newest",    "message": "Estimate c3e8a1f4-7b2d-4c6e-9a0f-5d1b8e3c2a74 isn't the project's newest. Send answers to the newest estimate."  }}