Writing a customer metafield through our API

Matt_Werdean
Shopify Partner
20 0 6

Hello,

I built an embedded app for my company that hit a bump in the road. Our store relies on this app to get everything done due to everything needing metafields. A part of the app attaches new metafields to existing customers, but when I post to 

 

https://${store}/admin/customers/${owner_id}.json
 
I get a 401 error. I have all of the scopes for this app approved, is this not an option for apps? Will I have to use private app credentials? If so it is a real pain because we own three stores that all do the same thing (school system with different schools), with many more coming in the future, and I don't want to hard code new credentials every time we get a new store.
Replies 8 (8)

KarlOffenberger
Shopify Partner
1873 184 900

Hi,

 

Metafields endpoint for the customer resource is not on

https://${store}/admin/customers/${owner_id}.json

It is on

https://${store}/admin/customers/${owner_id}/metafields.json

Let's say with some random metafield request body such as

{
  "metafield": {
    "namespace": "person",
    "key": "detail",
    "value_type": "json_string",
    "value": "{\"middlename\":\"john\"}"
  }
}

 

As long as your app is using a valid X-Shopify-Access-Token the POST should work fine and I've validated it does on my dev app.

 

Hope this helps!

 

 

 

Matt_Werdean
Shopify Partner
20 0 6

But what I am trying to do is add a metafield to and existing customer. I got this code directly from the docs:

 

Add metafield to an existing customer
PUT /admin/customers/#{customer_id}.json
{
  "customer": {
    "id": 207119551,
    "metafields": [
      {
        "key": "new",
        "value": "newvalue",
        "value_type": "string",
        "namespace": "global"
      }
    ]
  }
}

It actually does work fine using private app credentials but not app credentials 

Matt_Werdean
Shopify Partner
20 0 6

After trying the other way, I'm still getting the same problem. A 401 error. 

method: 'post',
     header:
      { 'X-Shopify-Access-Token': '*************************************' },
     url:
      'https://********.myshopify.com/admin/customers/************/metafields.json',
     data:
      '{"metafield":{"namespace":"fee","key":"child1_fee1","value":"****************","value_type":"string"}}' },
KarlOffenberger
Shopify Partner
1873 184 900

The example I posted above also adds a metafield to an existing customer and works.

KarlOffenberger
Shopify Partner
1873 184 900

Hmm... okay. Sorry, posted same time. Don't know why you're getting a 401 now. Are you using online access mode and are you sure your access token has not expired?

Matt_Werdean
Shopify Partner
20 0 6

Ahhh that could be it, I wasn't aware the access token expired. I thought the access token was generated when the app is installed? I have it stored, Is there another way to get a new one?

 

Edit: I am using offline access mode

I am getting:

{ errors:
         '[API] Invalid API key or access token (unrecognized login or wrong password)' } } }

 Which is weird because before I post I do a get request to get all customer metafields with the same exact access token.

Edit: I'm also doing other calls using the same token and am getting information back, so i'm not sure what the problem is. Maybe scopes? I thought I had approved everything:

APP PERMISSIONS
This app can access and modify your store‘s data.

Modify store content like articles, blogs, comments, pages, and redirects

Modify theme templates and theme assets

Modify products, variants, and collections

Modify customer details and customer groups

Modify orders, transactions, and fulfillments

Read all orders

Modify draft orders

Modify inventory

Read locations

Modify script tags in your store's theme template files

Modify fulfillment services

Modify shipping rates, countries, and provinces

Read analytics

Modify checkouts

Modify reports

Modify price rules

Modify marketing events and related marketing engagement data

Modify resource feedbacks

Read Shopify Payments balance and payouts

 

d0tmatrix
Tourist
3 0 3

Hi Karl,

 

I've had a hard time finding information about this newish feature of metafields, and some confusing bits too, as this section of the same documentation (Customer section) seems to suggest a different approach:

 

Add metafield to an existing customer

 

PUT /admin/api/2019-04/customers/#{customer_id}.json
{
  "customer": {
    "id": 207119551,
    "metafields": [
      {
        "key": "new",
        "value": "newvalue",
        "value_type": "string",
        "namespace": "global"
      }
    ]
  }
}

 

I've not been able to get the above to go through, only 406 errors that way.

 

Using your snippet, I can manually type out an array that is accepted by the Shopify API.

 

For instance, I'm trying to save a list of emails in a customer metafield and if I paste this as the "value", it goes through:

 

"[\"guy@buddy.co\", \"friend@friend.co\", \"friend@friend.com\"]"

 

but if I use javascript code (node v10) to join two lists of emails together (where both vars are arrays)

 

"value": existing_list.concat(new_invites)

 

nothing is updated and no error is thrown... So I wonder what to make of that.

 

If you've any thoughts or notice anything would be much appreciated, I can't find any documentation on working with arrays as the value of a "json_string" metafield type :S

 

Complete snippet:

 

    let data = await fetch(`${shopifyAPI}/customers/${customerId}/metafields.json`, {
      "method": "POST",
      "headers": postHeaders,
      "body": JSON.stringify({
        "metafield": {
          "namespace": "referrals",
          "key": "invited_emails",
          "value": emailsArray,
          "value_type": "json_string"
        }
      })
    })

 

Best

 

Michel

d0tmatrix
Tourist
3 0 3

For anyone trying this too, I am able to update array metafields like so:

 

    let data = await fetch(`${shopifyAPI}/customers/${customerId}/metafields.json`, {
      "method": "POST",
      "headers": postHeaders,
      "body": JSON.stringify({
        "metafield": {
          "namespace": "referrals",
          "key": toUpdate,
          "value": JSON.stringify(existing.concat(emails.filter(e => !existing.includes(e)))),
          "value_type": "json_string"
        }
      })
    })

Where toUpdate is passed in to the function, existing and emails are both arrays of strings and postHeaders is 

const postHeaders = { "Accept": "application/json", "Content-Type": "application/json" }