Create credentials with external secrets via API

Describe the problem/error/question

Hi folks,

how to create credentials referring external secrets and variables via n8n APIs?

I tried with both the n8n node and the http node. Here is the http node example. For the body, I tried to set the following expression:

{
"name": "{{ $('Get row(s) in sheet').item.json['REDIS CREDENTIAL NAME'] }}",
"type": "redis",
"data": {
"password": "{{ $('Get row(s) in sheet').item.json['REDIS CREDENTIAL PASSWORD'] }}",
"user": "{{ $('Get row(s) in sheet').item.json['REDIS CREDENTIAL USERNAME'] }}",
"host": "{{$vars.ENV_n8nRedisInstanceHost}}",
"port": {{$vars.ENV_n8nRedisInstancePort.toNumber()}},
"database": {{$vars.ENV_n8nRedisInstanceDbNum.toNumber()}},
"ssl": true,
"disableTlsVerification": false
},
"isResolvable": false,
"projectId": "{{ $('Create project').item.json.id }}"
}

which produced the following body:

{
"name": "JENG_REDIS",
"type": "redis",
"data": {
"password": "{{ $secrets.n8n.n8n['cwkl-services-aws'].JENG_REDIS_PASS }}",
"user": "{{ $secrets.n8n.n8n['cwkl-services-aws'].JENG_REDIS_USER }}",
"host": "``master.n8n-ai-memory.ca0pia.euc1.cache.amazonaws.com``",
"port": 6379,
"database": 0,
"ssl": true,
"disableTlsVerification": false
},
"isResolvable": false,
"projectId": "4mc5UAoKkScqmwpr"
}

The credential was created successfully, but the external secrets references are set as plain text and not as expressions, see screenshot below.

I want to obtain a credential like the following:

How can I do?

What is the error message (if any)?

Please share your workflow

(Select the nodes on your canvas and use the keyboard shortcuts CMD+C/CTRL+C and CMD+V/CTRL+V to copy and paste the workflow.)

Share the output returned by the last node

Information on your n8n setup

  • n8n version: 2.39.6
  • Database (default: SQLite): PostgreSQL
  • n8n EXECUTIONS_PROCESS setting (default: own, main): queue
  • Running n8n via (Docker, npm, n8n cloud, desktop app): Docker
  • Operating system: Linux

Hey @Lorenzo_Tacconi1, while you wait for a response, here are some things that might help:

Suggested resources

Automatically matched to your question.

Docs:

Forum:

@Anshul_Namdev, @ihortom, @barn4k - you’ve helped with similar issues before, can you take a look?

Automatically suggested by n8n’s community bot. It’s a pilot - please share feedback here.

@Lorenzo_Tacconi1 Top of the day to you!

To force n8n to treat credentials field as expression instead of evaluating them immediately or saving them as plain text via the API, you need to use the hidden _metadata property inside the data object.

By default , when you submit fields wrapped in {{…}} via an n8n API or HTTP node payload , n8n treats them as raw strings values. To tell the backend UI to toggle the respective input blocks into expression mode (as seen in your second screenshot), you must explicitly flag them into the metadat schema.

The Solution

Update your API request body payload to include the _metadata configuration pointing to your target fields .

modified payload

{
  "name": "{{ $('Get row(s) in sheet').item.json['REDIS CREDENTIAL NAME'] }}",
  "type": "redis",
  "data": {
    "password": "{{ $('Get row(s) in sheet').item.json['REDIS CREDENTIAL PASSWORD'] }}",
    "user": "{{ $('Get row(s) in sheet').item.json['REDIS CREDENTIAL USERNAME'] }}",
    "host": "{{$vars.ENV_n8nRedisInstanceHost}}",
    "port": {{$vars.ENV_n8nRedisInstancePort.toNumber()}},
    "database": {{$vars.ENV_n8nRedisInstanceDbNum.toNumber()}},
    "ssl": true,
    "disableTlsVerification": false,
    "_metadata": {
      "expressionProperties": {
        "password": true,
        "user": true,
        "host": true,
        "port": true,
        "database": true
      }
    }
  },
  "isResolvable": false,
  "projectId": "{{ $('Create project').item.json.id }}"
}

Unfortunately by running that I received error 400 with the following message:
400 - "{\"message\":\"request.body.data is not allowed to have the additional property \\\"_metadata\\\"\"}"

May it be that that property is not supported anymore? I tried to move the metadata node outside of “data” but it didn’t work. The credential was created, in that case, but the values were plain text as before and not expressions.

Hi @Lorenzo_Tacconi1, remove _metadata. The missing part is the leading =: n8n stores expressions as ={{ ... }}, not just {{ ... }}.

To preserve the references without evaluating them in the HTTP Request node, build the body in a Code node set to Run Once for Each Item:

const row = $('Get row(s) in sheet').item.json;

return {
  json: {
    name: row['REDIS CREDENTIAL NAME'],
    type: 'redis',
    data: {
      password: '=' + row['REDIS CREDENTIAL PASSWORD'],
      user: '=' + row['REDIS CREDENTIAL USERNAME'],
      host: '={{ $vars.ENV_n8nRedisInstanceHost }}',
      port: Number($vars.ENV_n8nRedisInstancePort),
      database: Number($vars.ENV_n8nRedisInstanceDbNum),
      ssl: true,
      disableTlsVerification: false
    },
    isResolvable: false,
    projectId: $('Create project').item.json.id
  }
};

This assumes your sheet cells contain the {{ $secrets... }} references shown above. In HTTP Request, use JSON body mode and set the entire body to the expression {{ $json }}.

One limitation: in 2.39.6 the public API requires numbers for port and database, so those remain fixed here. If you need them as variable expressions too, create the credential first and switch those two fields in the UI.

Thank you @Anshul_Namdev, that was very useful. As you mentioned the key was to add the '=' sign at the beginning of the values. I was able to do it without passing through a code node. I just added a small set fields node before to be able to set the variable path {{ $vars.ENV_n8nRedisInstanceHost }} as a node output to be referenced in the http request node to create the credential.

Here is the body that I set in the http request node.

{
  "name": "{{ $('Get row(s) in sheet').item.json['REDIS CREDENTIAL NAME'] }}",
  "type": "redis",
  "data": {
  "password": "={{ $('Get row(s) in sheet').item.json['REDIS CREDENTIAL PASSWORD'] }}",
  "user": "={{ $('Get row(s) in sheet').item.json['REDIS CREDENTIAL USERNAME'] }}",
  "host": "={{ $json.host }}",
  "port": {{ $vars.ENV_n8nRedisInstancePort }},
  "database": {{ $vars.ENV_n8nRedisInstanceDbNum }},
  "ssl": true,
  "disableTlsVerification": false
},
  "isResolvable": false,
  "projectId": "{{ $('Create project').item.json.id }}"
} 

As you said it’s not possible to use this technique for port and database because the api expects numeric values, so they end up to be fixed values and not variable references in the generated credential, however for my case this is ok. It’s safe to assume that even if we will migrate the redis instance the port and db number will remain the same. They’re the default for Redis.

Thank you very much for your help!!