API Issue: Posting A Card

Hi all! I’m trying to implement my own C# client and I’ve run into this wall: I can’t seem to run a new/unsaved/custom query from my client. From what I can tell of the docs, this would require a POST action to the “api/card” endpoint. I’m actually modeling my client after another library written in Python. Following is the structure of the payload used in their POST (apologies for the length of these code snippets):

    request_data = {
            "name": name,
            "display": "scalar",
            "visualization_settings": {
            },
            "dataset_query": {
                "database": database_id,
                "type": "native",
                "native": {
                    "query": query,
                    "collection": kwargs.get('collection', None),
                    "template_tags": kwargs.get('template_tags', {})
                }
            },
            "description": kwargs.get('description', None),
            "collection_id": kwargs.get('collection_id', None)
        }

It may be that I’m not that advanced in C# but structuring a string literal (with arguments) for JSON is a bit tricky… but in the VS debug view, the resulting JSON string is valid. Unfortunately, that payload does not work for the “api/card” endpoint, and I’ve been trying variations on that payload for a week. I just discovered through the browser network inspector that the Metabase site seems to post a slightly different payload structure to the “api/dataset”, their JSON looks like the following:

{"native":{"query":"<query>";","template-tags":{}},"type":"native","database":2,"parameters":[]}

Then I found that the documentation only list “database” and “query” as required parameters. I’ve tried both of those structures with no luck as well. All of my attempts result in a “400: Bad Request” error.

Does anyone have experience with these POST requests and payloads in C#, or know if I’m missing anything? Also let me know if you need further clarification or examples.

Thank you!

Hi @WalterStudios

When you want to run a query, but not save it as a question, then you POST /api/dataset.

The best way to learn how to use the API, as you’ve learned, is simply by using the Metabase interface and just check the requests in the browser developer Network-tab.

Example, this POST /api/dataset would get the first two records from Sample Dataset’s Orders:
{"native":{"query":"SELECT * FROM \"ORDERS\" LIMIT 2","template-tags":{}},"type":"native","database":1,"parameters":[]}

So what’s the difference between that and your example - nothing from what I can see, besides a different database and query. So I’m not sure why it isn’t working, but have you checked the Metabase log for more details on the error?

Have you tried to “Copy as cURL” (right-click the request in browser Network-tab) and testing with curl (a known, very well documented tool)? I removed some of unneeded parameters, and of course, you need to use your session token:
curl 'http://localhost:3000/api/dataset' -H 'Content-Type: application/json' -H 'Cookie: metabase.SESSION=fa856cf2-4a1a-4dbe-b5f5-e6b7e2e1d065' --data '{"native":{"query":"SELECT * FROM \"ORDERS\" LIMIT 2","template-tags":{}},"type":"native","database":1,"parameters":[]}'

Cool, I’ll see what I can do with this!
Also, I don’t believe I would have access to the log as I’m accessing Metabase that’s hosted on an Amazon server (for the job) I believe.

@WalterStudios If you have access to the server (command line), then you have access to the log. But the log is also available in the interface via Admin > Troubleshooting > Logs

That’s the thing… I’m not even in the IT/Data team lmao. I’m in customer support, but I use coding/scripting to be more efficient. I’ve tried asking for similar access and they’re not budging lol, gotta make use of what I have. I’ll keep that in mind though.

1 Like

Thank you, I figured it out!!! I was wrapping the database Id in quotes instead of leaving it as an integer. I honestly wouldn’t have figured that out without curl since it actually shows the error that Metabase returns… This changes everything… You’re the best @flamber!

1 Like

I found it easier to create the api calls by using a dictionary, them converting to JSON. Nicer to read than lots of strong concatenation, but probably a personal preference.

1 Like

That’s interesting! I’ll try to play around with that and see if json.net can convert it to a string.

Just checked my code. Most of the dictionary stuff I’ve written is to do with integrating a SMS system, but this is what creates the token for the embedded url:

private string GetToken(Int32 dashboardId)
    {
        var dash = new Dictionary<string, Int32>
        {
            { "dashboard", dashboardId }
        };
        var pars = new Dictionary<string, string[]>();
        pars = GetFilters();
        JwtPayload payload = new JwtPayload
    {
            {"resource",dash } ,
            {"params" ,pars }
        };
        return new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(new JwtHeader(new Microsoft.IdentityModel.Tokens.SigningCredentials(new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(Encoding.UTF8.GetBytes(ConfigurationManager.AppSettings["authKey"])), SecurityAlgorithms.HmacSha256Signature)), payload));
    }

    private Dictionary<string, string[]> GetFilters()
    {
        var filters = new Dictionary<string, string[]>();
        AddFilter("team", teamList.Items, filters);
        return filters;
    }

Teamlist is just a dropdown list in the web page hosting the Metabase IFRAME. If you’re used to a ‘proper’ language like C#, this makes much more sense than joining up lots of strings.

2 Likes

Yeah, I just implemented a dictionary approach as well:

Dictionary<string, object>();

From there I just add the parameters as key-value pairs through the .Add() method. Used Type object in the Dict definition since some of the parameters are empty or would require anonymous objects to allow for sub-parameters. Then convert it with JsonConvert.SerializeObject(). Muuuccchhh easier and cleaner than escaping/joining strings.